Taking list of elements as param in Class method - python

I have a class Dbcrud() that I will outline below.
I want to take several parameters from a method: db_select() one being selected_fields which would be a list of fields.
Im having trouble forming my method db_select() to allow multiple fields to be defined for selected_fields.
Can someone help?
Thank you
UPDATED
class DbCrud:
query_stmt_list = ['SELECT','INSERT','UPDATE','DELETE','FROM','WHERE']
def __init__(self):
self.query_stmt_list = DbCrud.query_stmt_list
self.query_stmt_list = query_stmt_list
def set_db_settings(self, host, username, passwd, database):
self.host = host
self.username = username
self.passwd = passwd
self.database = database
db = pymysql.connect(host=host, user=username, passwd=passwd, db=database)
return db
def db_select(self, selected_fields, table, where_field):
self.selected_fields = selected_fields
self.table = table
self.where_field = where_field
try:
with db.cursor() as cursor:
sql_tld_id_query = self.query_stmt_list[0] + selected_fields* + self.query_stmt_list[4] + table + self.query_stmt_list[5] + where_field + '=' + %s
cursor.execute(sql_tld_id_query, (self.site_search_url,))
tld_id_value = cursor.fetchone()
except:
db.rollback()
pass

You have few issues here:
1.
You need that row at the init method (as this is the c'tor of the class)
Also as query_stmt_list is a static member, you should access him with the class name as prefix.
def __init__(self):
self.query_stmt_list = DbCrud.query_stmt_list
2.
You can't define a function param with selected_fields[], it's a syntax error, you can pass to selected_fields whatever you like.
def db_select(self, selected_fields, table, where_field):
3.
When you try to use the variable query_stmt_list (at the following line of code i've attached), do you mean you want the class member or the instance member?
If instance you should change it to self.query_stmt_list
If the class member, you should change it to DbCrud.query_stmt_list
sql_tld_id_query = query_stmt_list[0] + selected_fields* + query_stmt_list[4] + table + query_stmt_list[5] + where_field + '=' + %s
Also, in order to loop though the selected_fields you could do:
query_stmt_list[0] + ", ".join(item for item in self.selected_fields) + query_stmt_list[4] ...

You can always expect selected_fields to be a list so you can use ', '.join:
def db_select(self, selected_fields, table, where_field):
query = 'select {selected_fields} {table} where {where_field} = 1'
query = query.format(selected_fields=', '.join(selected_fields),
table=table, where_field=where_field)
.
.
obj.db_select(['col_a'], 'table_a', 'where_field_a')
Note that this is vulnerable to SQL injection, but so is your original code (if it wasn't for the syntax errors it currently has).

Related

Passing a variable as a list of arguments

My code is below. I'm just getting the basic functionality working before making more of the database, but I've run into a problem - Is there any way to pass a variable into four different arguments? My create_person function uses four arguments, but I need to initiate this after I create a Person object.
import os
import sqlite3
from personClass import *
#Connection to database - ToDoDB.db
conn = sqlite3.connect('ToDoDB.db')
cursor = conn.cursor()
def create_table_ToDo():
cursor.execute("""CREATE TABLE ToDo (
Forename text,
Surname text,
FirstChore text,
SecondChore text
)""")
print("ToDo table successfuly created!")
conn.commit()
def create_person(Forename, Surname, FirstChore, SecondChore):
query= "INSERT INTO ToDo (Forename, Surname, FirstChore, SecondChore)values (?,?,?,?);" #Inserts values below into this - question mark to sanitize first -
cursor.execute(query,(Forename, Surname, FirstChore, SecondChore)) #- then executes this command
conn.commit() #Commit and close after each function to save
print("New person and tasks added to the list of users")
#create_table_ToDo()
johnTest = Person("John", "Test", "Ironing clothes", "Washing dishes")
print(johnTest)
create_person(johnTest)
I've included my person class here just in case it helps.
class Person(ToDo):
def __init__(self, firstName, lastName, choreOne, choreTwo):
super().__init__(choreOne, choreTwo)
self.firstName = firstName
self.lastName = lastName
def getName(self):
print("My name is " + self.firstName + " " + self.lastName)
def getTasks(self):
print("My name's " + self.firstName + " and my tasks are " + self.choreOne + ", " + self.choreTwo)
def __repr__(self):
response = "{},{},{},{}".format(
self.firstName,
self.lastName,
self.choreOne,
self.choreTwo)
return response
You can use python's built-in getattr method to access the attribute values of an object. The following line should work:
create_person(getattr(johnTest, 'firstName'), getattr(johnTest, 'lastName'), getattr(johnTest, 'choreOne'), getattr(johnTest, 'choreTwo'))

Class variables will not overwritten by funktions

I'm trying to read out a database for a login-portal. This part is working in a class dbreader.
The username and password will be saved in the variables cUsername and cUserpass. Before they get overwritten by a function the variables are defined as empty strings "". \
But when I try this: username = dbreader().cUsername, I get an empty string.
Here is the code:
class dbreader:
db_path = "Home-Tool/UserDB"
cUsername = ""
cUserpass = ""
cName = ""
id_range_bool = False
iddb = 1
def __init__(self):
self.connection = _sqlite3.connect(self.db_path)
self.c = self.connection.cursor()
def close(self):
self.connection.close()
def run_sqlCommand(self):
while True:
sqlcom = "SELECT * FROM userinfos WHERE id = " + str(self.iddb)
self.c.execute(sqlcom)
self.connection.commit()
for row in self.c.fetchall(): # self.c = tuble(4)
self.cID = str(row[0])
self.cUsername = str(row[1])
self.cUserpass = str(row[2])
self.cName = str(row[3])
if self.cUsername == cEntryUsername:
break
if not self.id_range_bool:
break
else:
self.id_range_bool = False
self.iddb += 1
dbreader().run_sqlCommand()
username = dbreader.cUsername
userpass = dbreader.cUserpass
name = dbreader.cName
Since dbreader is a class, accessing an attribute will return it as a class variable, not as an instance. The way you have it set up, dbreader.cUsername and dbreader.cPassword will never change, because nothing overwrites them.
On a particular instance of a dbreader, these attributes are overwritten. But you have to use the same instance:
dbreader_instance = dbreader() # create an instance, save the instance to a variable
dbreader_instance.run_sqlCommand()
username = dbreader_instance.cUsername
userpass = dbreader_instance.cUserpass
name = dbreader_instance.cName
You could then create another, separate instance of dbreader, and it might have a different cUsername and cUserpass from the one above.

How to find last inserted bookId from table in sqllite database using Python code?

import sqlite3
from sqlite3 import Error
class database_tasks:
bookId = 0
studentId = 0
def create_connection(self):
try:
conn = sqlite3.connect('library Management.db')
return conn
except Error as e:
print(e)
def insert(self, conn,dbname, name, writer='none'):
c = conn.cursor()
if dbname == "Book_database":
database_tasks.bookId += 1
#sql= "INSERT INTO Book_database VALUES(" + database_tasks.bookId + "," + name + "," + writer + ")"
c.execute ("INSERT INTO Book_database VALUES(?,?,?)",(database_tasks.bookId, name, writer))
The bookId always set to 1 whenever new record is inserted. But I want to insert record with bookId 1 greater than the last inserted record.
How do I initialize this bookId class variable to one more than the last inserted record?
import sqlite3
from sqlite3 import Error
class database_tasks:
def __init__(self,bookId=0,studentId=0):
self.bookId=bookId
self.studentId=studentId
def create_connection(self):
try:
conn = sqlite3.connect('library Management.db')
return conn
except Error as e:
print(e)
def insert(self, conn,dbname, name, writer='none'):
c = conn.cursor()
if dbname == "Book_database":
self.bookId += 1
#sql= "INSERT INTO Book_database VALUES(" + database_tasks.bookId + "," + name + "," + writer + ")"
c.execute ("INSERT INTO Book_database VALUES(?,?,?)",(database_tasks.bookId, name, writer))
you can initialize class as:
data = database_tasks()
or
data = database_tasks(bookId=<id>,studentId=<id>)
and insert records:
data.insert(coon,dbname,name,writer)
let me know if this help.
I'm not sure I understand your question. You are already declaring bookId as a class variable/ static variable. Getters and setters don't work in python like they do in languages like java. But for a class variable, you wouldn't need a getter and setter method. If you would like to change a class variable, you can access it outside of the class with a call to database_tasks.bookId.

Accepting list as a param in class method - name 'selected_fields' is not defined

I have a class Database that looks like:
UPDATED
class Database():
query_stmt_list = ['SELECT','INSERT','UPDATE','DELETE','FROM','WHERE']
def db_select(self, *selected_fields, **kwargs):
self.selected_fields = selected_fields = list(selected_fields)
self.table = (kwargs['table']
if 'table' in kwargs
else selected_fields.pop())
self.where_field = (kwargs['where_field']
if 'where_field' in kwargs
else selected_fields.pop())
try:
with db.cursor() as cursor:
sql_tld_id_query = self.query_stmt_list[0]+ selected_fields + self.query_stmt_list[4] + table + self.query_stmt_list[5] + where_field + '=' + 'www.website.com'
except Exception as gatherid_err:
print("exception was {}".format(gatherid_err))
db.rollback()
I've invoking this via:
dbclass = Database()
dbclass.db_select(*selected_fields, table='tld', where_field='name')
But I get an error of:
line 51, in <module>
dbclass.db_select(*selected_fields, table='tld', where_field='name')
NameError: name 'selected_fields' is not defined
Thank you.
If you want to unpack the "selected_fields" variable like dbclass.db_select(*selected_fields, 'tld', 'name') did, you'd better:
Have Python 3.5/3.6 installed. The syntax like this function call was wrong in Python 3.4 and before. The new feature introduced by 3.5 was called "Additional Unpacking Generalizations" and it allows user to put the variable that decorated by iterable unpacking operator, aka the single asterisk "*", before other variables.
Modify your db_select method to apply a trick that can accept two extra parameters after the *selected_fields like this:
def db_select(self, *args):
self.selected_fields = selected_fields = list(args)
self.where_field = selected_fields.pop()
self.table = selected_fields.pop()
...
If you want the db_select method to accept named parameters too, for example dbclass.db_select(*selected_fields, table='tld', where_field='name'), it can be done like this:
def db_select(self, *args, **kwargs):
self.selected_fields = selected_fields = list(args)
self.where_field = (kwargs['where_field']
if 'where_field' in kwargs
else selected_fields.pop())
self.table = (kwargs['table']
if 'table' in kwargs
else selected_fields.pop())
...

How to write tests for generic method used to insert queries

I have a database class and this class contains a method used to insert records. This is how the method looks:
def insertRecord(self, **kwargs):
if 'table' not in kwargs.keys():
raise Exception('The table keyword is required')
table = kwargs['table']
del kwargs['table']
query_fields = kwargs.keys()
pg_fields = []
for field in query_fields:
pg_fields.append('%(' + field + ')s')
query_field_string = ', '.join(query_fields)
query_pg_string = ', '.join(pg_fields)
self.cur.execute('INSERT INTO ' + table + '(' +
query_field_string + ') VALUES (' + query_pg_string + ')',
kwargs
)
self.conn.commit()
The method accepts variable arguments list so the user can use this method to insert entries in any table. Bassically, the method is constructing a query string of the form INSERT INTO <table>(<field1>, <field2>...) VALUES (%(field1)s, %(field2)s...), since the execute method accepts as the second argument a dictionary of the form <field>: <value> all the strings of the form %(field)s will be replaced with the corresponding value.
Basically, the method works fine, but I don't know how should I test it. Should I make a test database and see if the values passed to it are in the database after calling it? How would you write tests for such a method?
Refactor the code to format the SQL command, then test that. In this way it's much simpler -- pass in args, get a formatted string and a dictionary back. No mocking needed.
source
# python -m unittest insertrec
import unittest
def formatInsert(table, **kwargs):
assert table
query_fields = kwargs.keys()
pg_fields = []
for field in query_fields:
pg_fields.append('%(' + field + ')s')
query_field_string = ', '.join(query_fields)
query_pg_string = ', '.join(pg_fields)
return (
'INSERT INTO ' + table + '(' +
query_field_string + ') VALUES (' + query_pg_string + ')',
kwargs
)
class TestInsert(unittest.TestCase):
def test_err_notable(self):
self.assertRaises(AssertionError, formatInsert, None)
def test_insert1(self):
self.assertEquals(
formatInsert('mytab', beer='tasty'),
('INSERT INTO mytab(beer) VALUES (%(beer)s)',
{'beer': 'tasty'}
),
)

Categories

Resources