Executing multiple SQL queries with Python Flask - python

I have a python function which should execute 2 SQL queries. I have found that it is impossible to execute 2 queries in one command at once, so as a workaround I created a list of my queries and try to iterate over it with execute command. However nothing is added to MySQL table. Here is the code:
#app.route('/addComment', methods=['POST'])
def addComment():
try:
if session.get('user'):
_description = request.form['description']
_user = session.get('user')
_term_id = request.form['termID']
_time = datetime.now()
operation = ['"INSERT INTO comments (description, user, termID, time) VALUES (%s, %s, %s, %s)", (_description, _user, _term_id, _time)', '"INSERT INTO history (user, term, time) VALUES (%s, %s, %s)", (_user, _term_id, _time)']
conn = mysql.connect()
cursor = conn.cursor()
for item in operation:
cursor.execute()
conn.commit()
data = cursor.fetchall()
if len(data) == 0:
conn.commit()
return json.dumps({'status':'OK'})
else:
return json.dumps({'status':'ERROR'})
except Exception as e:
return json.dumps({'status':'Unauthorized access'})
finally:
cursor.close()
conn.close()
Could you please help me?

Errors in your code lies in the following areas:
A. On iteration sql statement is not passed to execute()
Should be:
for item in operation:
cursor.execute(item)
conn.commit()
B. Invalid parameterization
'"INSERT INTO comments (description, user, termID, time) VALUES (%s, %s, %s, %s)", (_description, _user, _term_id, _time)'
This string statement doesn't apply variables to SQL statement string. Depending on your value types you should decide whether to add ' (apostrophe) or not. More safely would be to pass parameters to .execute() function. Example below.
cursor.execute(
"INSERT INTO comments (description, user, termID, time) VALUES (:description, :user, :term_id, :time)",
description=_description,
user=_user,
term_id=_term_id,
time=_time
)

Related

Psycopg[binary] is turning my parameters into individual tuples rather than a single tuple

I am writing a basic banking app to keep my skills fresh and am trying to implement a transaction table into the mix to of course keep track of transactions. This happens as I am trying to insert a new transaction programmatically using Python and psycopg[binary]. I have it the same as I do for creating new accounts and customers so I am at a loss for words after trying everything I can think of including using a formatted string for my parameters and the same problem was occurring. I will include a clip of the code that has the unexpected behavior as well as clips where the code is similar and follows the expected behavior. I even forced an error in the good code so that it would show that it has the data in a single tuple.
def create_transaction(self, transaction: Transaction) -> Transaction:
logging.info("Beginning DAL function create transaction")
sql = "insert into banking.transactions values (default, %s, %s, %s, %s) returning transaction_id;"
cursor = connection.cursor()
cursor.execute(sql, (transaction.account_id, transaction.transaction_type, transaction.amount, transaction.time_and_date))
connection.commit()
transaction_id = cursor.fetchone()[0]
transaction.transaction_id = transaction_id
logging.info("Finishing DAL function create transaction")
return transaction
def create_customer(self, customer: Customer) -> Customer:
logging.info("Beginning DAL function create customer")
sql = "insert into banking.customers values (default, %s, %s, %s, %s) returning customer_id;"
cursor = connection.cursor()
cursor.execute(sql, (customer.first_name, customer.last_name, customer.username, customer.password))
connection.commit()
customer_id = cursor.fetchone()[0]
customer.customer_id = customer_id
logging.info("Finishing DAL function create customer")
return customer
def create_account(self, account: BankAccount) -> BankAccount:
logging.info("Beginning DAL function create account")
sql = "insert into banking.bank_accounts values (default, %s, %s) returning account_id;"
cursor = connection.cursor()
cursor.execute(sql, (account.customer_id, account.balance))
connection.commit()
account_id = cursor.fetchone()[0]
account.account_id = account_id
logging.info("Finishing DAL function create account")
return account
query = 'insert into banking.bank_accounts values (default, %d, %s) returning account_id;'
params = (-1, 50.0)
query = 'insert into banking.transactions values (default, %s, %s, %s, %s) returning transaction_id;'
params = ((-1,), ('deposit',), (50.0,), '2022-11-28 21:56:22.000486')
def execute(
self: _Self,
query: Query,
params: Optional[Params] = None,
*,
prepare: Optional[bool] = None,
binary: Optional[bool] = None,
) -> _Self:
"""
Execute a query or command to the database.
"""
try:
with self._conn.lock:
self._conn.wait(
self._execute_gen(query, params, prepare=prepare, binary=binary)
)
except e.Error as ex:
raise ex.with_traceback(None)
E psycopg.errors.InvalidTextRepresentation: invalid input syntax for integer: "(-1)"
I have it the same as I do for creating new accounts and customers so I am at a loss for words after trying everything I can think of including using a formatted string for my parameters and the same problem was occurring. I have tried typecasting it to an integer before using the execute method but it happens within that line of code.

Python "INSERT INTO" vs. "INSERT INTO...ON DUPLICATE KEY UPDATE"

I am trying to use python to insert a record into a MySQL database and then update that record. To do this I have created 2 functions:
def insert_into_database():
query = "INSERT INTO pcf_dev_D.users(user_guid,username) VALUES (%s, %s) "
data = [('1234', 'user1234')]
parser = ConfigParser()
parser.read('db/db_config.ini')
db = {}
section = 'mysql'
if parser.has_section(section):
items = parser.items(section)
for item in items:
db[item[0]] = item[1]
else:
raise Exception('{0} not found in the {1} file'.format(section, filename))
try:
conn = MySQLConnection(**db)
cursor = conn.cursor()
cursor.executemany(query, data)
conn.commit()
except Error as e:
print('Error:', e)
finally:
# print("done...")
cursor.close()
conn.close()
This works fine and inserts 1234, user1234 into the db.
Now I want to update this particular user's username to '5678', so I have created another function:
def upsert_into_database():
query = "INSERT INTO pcf_dev_D.users(user_guid,username) " \
"VALUES (%s, %s) ON DUPLICATE KEY UPDATE username='%s'"
data = [('1234', 'user1234', 'user5678')]
parser = ConfigParser()
parser.read('db/db_config.ini')
db = {}
section = 'mysql'
if parser.has_section(section):
items = parser.items(section)
for item in items:
db[item[0]] = item[1]
else:
raise Exception('{0} not found in the {1} file'.format(section, 'db/db_config.ini'))
try:
conn = MySQLConnection(**db)
cursor = conn.cursor()
cursor.executemany(query, data)
conn.commit()
except Error as e:
print('Error:', e)
finally:
# print("done...")
cursor.close()
conn.close()
Which produces the following error:
Error: Not all parameters were used in the SQL statement
What's interesting is if I modify query and data to be:
query = "INSERT INTO pcf_dev_D.users(user_guid,username) " \
"VALUES (%s, %s) ON DUPLICATE KEY UPDATE username='user5678'"
data = [('1234', 'user1234')]
Then python updates the record just fine...what am I missing?
You included the 3rd parameter within single quotes in the update clause, therefore it is interpreted as part of a string, not as a placeholder for parameter. You must not enclose a parameter by quotes:
query = "INSERT INTO pcf_dev_D.users(user_guid,username) " \
"VALUES (%s, %s) ON DUPLICATE KEY UPDATE username=%s"
UPDATE
If you want to use the on duplicate key update clause with a bulk insert (e.g. executemany()), then you should not provide any parameters in the update clause because you can only have one update clause in the bulk insert statement. Use the values() function instead:
query = "INSERT INTO pcf_dev_D.users(user_guid,username) " \
"VALUES (%s, %s) ON DUPLICATE KEY UPDATE username=VALUES(username)"
In assignment value expressions in the ON DUPLICATE KEY UPDATE clause, you can use the VALUES(col_name) function to refer to column values from the INSERT portion of the INSERT ... ON DUPLICATE KEY UPDATE statement. In other words, VALUES(col_name) in the ON DUPLICATE KEY UPDATE clause refers to the value of col_name that would be inserted, had no duplicate-key conflict occurred. This function is especially useful in multiple-row inserts. The VALUES() function is meaningful only in the ON DUPLICATE KEY UPDATE clause or INSERT statements and returns NULL otherwise.

Executing multi-table queries with Python MySQLdb

I have been trying to return the values from two different tables, but can't seem to get the c.execute(query) function to return what I want it to. Currently my code will return the first c.fetchone()[0], but the second fetchone()[5] gives an error that it's out of range, which means it is probably still trying to get data from my 'clients' table which does not have 6 columns. I don't think I fully understand how MySQLdb works it's magic, but can't find any good examples of multi-table queries. Here is my code snippet below! Thanks!
c, conn = connection()
#check if already exists
x = c.execute("SELECT * FROM clients WHERE email = (%s)", (thwart(email),))
if int(x) > 0:
flash("That email already has an account, please try a new email or sign in.")
return render_template('register.html', form=form)
else:
c.execute("INSERT INTO clients (email, phone, password) VALUES (%s, %s, %s)", (thwart(email), thwart(phone), thwart(password)))
c.execute("SELECT cid FROM clients WHERE email = (%s)", (thwart(email),))
clientcid = c.fetchone()[0]
c.execute("INSERT INTO cpersonals (first_name, last_name, address, zip) VALUES (%s, %s, %s, %s)", (thwart(first_name), thwart(last_name), thwart(address), czip))
c.execute("SELECT reg_date FROM cpersonals WHERE cid = (%s)", (clientcid,))
reg_date = c.fetchone()[5]
rating = c.execute("SELECT rating FROM clients WHERE email = (%s)", (thwart(email),))
conn.commit()
flash("Thanks for registering!")
c.close()
conn.close()
Your query is SELECT reg_date FROM cpersonals .... You are only selecting one column. The reason fetchone()[5] fails is, there is no 6th column in the fetched record. Try 0 in place of 5.
Why were you using 5?

MySQL not accepting executemany() INSERT, running Python from Excel (datanitro)

I HAVE ADDED MY OWN ANSWER THAT WORKS BUT OPEN TO IMPROVEMENTS
After seeing a project at datanitro. I took on getting a connection to MySQL (they use SQLite) and I was able to import a small test table into Excel from MySQL.
Inserting new updated data from the Excel sheet was this next task and so far I can get one row to work like so...
import MySQLdb
db = MySQLdb.connect("xxx","xxx","xxx","xxx")
c = db.cursor()
c.execute("""INSERT INTO users (id, username, password, userid, fname, lname)
VALUES (%s, %s, %s, %s, %s, %s);""",
(Cell(5,1).value,Cell(5,2).value,Cell(5,3).value,Cell(5,4).value,Cell(5,5).value,Cell(5,6).value,))
db.commit()
db.close()
...but attempts at multiple rows will fail. I suspect either issues while traversing rows in Excel. Here is what I have so far...
import MySQLdb
db = MySQLdb.connect(host="xxx.com", user="xxx", passwd="xxx", db="xxx")
c = db.cursor()
c.execute("select * from users")
usersss = c.fetchall()
updates = []
row = 2 # starting row
while True:
data = tuple(CellRange((row,1),(row,6)).value)
if data[0]:
if data not in usersss: # new record
updates.append(data)
row += 1
else: # end of table
break
c.executemany("""INSERT INTO users (id, username, password, userid, fname, lname) VALUES (%s, %s, %s, %s, %s, %s)""", updates)
db.commit()
db.close()
...as of now, I don't get any errors, but my new line is not added (id 3). This is what my table looks like in Excel...
The database holds the same structure, minus id 3. There has to be a simpler way to traverse the rows and pull the unique content for INSERT, but after 6 hours trying different things (and 2 new Python books) I am going to ask for help.
If I run either...
print '[%s]' % ', '.join(map(str, updates))
or
print updates
my result is
[]
So this is likely not passing any data to MySQL in the first place.
LATEST UPDATE AND WORKING SCRIPT
Not exactly what I want, but this has worked for me...
c = db.cursor()
row = 2
while Cell(row,1).value != None:
c.execute("""INSERT IGNORE INTO users (id, username, password, userid, fname, lname)
VALUES (%s, %s, %s, %s, %s, %s);""",
(CellRange((row,1),(row,6)).value))
row = row + 1
Here is your problem:
while True:
if data[0]:
...
else:
break
Your first id is 0, so in the first iteration of the loop data[0] will be falsely and your loop will exit, without ever adding any data. What you probably ment is:
while True:
if data[0] is not None:
...
else:
break
I ended up finding a solution that gets me an Insert on new and allows for UPDATE of those that are changed. Not exactly a Python selection based on a single query, but will do.
import MySQLdb
db = MySQLdb.connect("xxx","xxx","xxx","xxx")
c = db.cursor()
row = 2
while Cell(row,1).value is not None:
c.execute("INSERT INTO users (id, username, password, \
userid, fname, lname) \
VALUES (%s, %s, %s, %s, %s, %s) \
ON DUPLICATE KEY UPDATE \
id=VALUES(id), username=VALUES(username), password=VALUES(password), \
userid=VALUES(userid), fname=VALUES(fname), lname=VALUES(lname);",
(CellRange((row,1),(row,6)).value))
row = row + 1
db.commit()
db.close()

getting error with execute many in python

I am learning python and i am new bie.
I am trying to use functions with mysql and python and i ma getting errors
This is my script
import MySQLdb
def insert_values(cursor, values):
#cursor = self.connection.cursor()
cursor.executemany("""
insert into pythontest (name1,name2,name3)
values (%s, %s, %s)""", values)
cursor.close()
db = MySQLdb.connect("localhost","root","root","python" )
cursor = db.cursor()
var1 = ['name1','name2','name3']
insert_values(cursor,var1)
db.close()
There may be many errors because i am learning
1)i don't know how can i pass db
object in function or passing cusrsor
is ok. because i have to call that
function many times in for loop
2)is the syntax of values array ok to
go in database
ERRORS
File "mysql.py", line 10, in insert_values
values (%s, %s, %s)""", values)
File "build/bdist.linux-i686/egg/MySQLdb/cursors.py", line 216, in executemany
File "build/bdist.linux-i686/egg/MySQLdb/connections.py", line 36, in defaulterrorhandler
_mysql_exceptions.ProgrammingError: not enough arguments for format string
cursor.executemany("""
insert into pythontest (name1,name2,name3)
values (%s, %s, %s)""", *values)
Here's how I would write that (But untested):
import MySQLdb
def insert_values(db, values):
cursor = db.cursor()
try:
try:
cursor.execute("""
insert into pythontest (name1,name2,name3)
values (%s, %s, %s)""", *values)
except:
db.rollback()
raise
else:
db.commit()
finally:
cursor.close()
db = MySQLdb.connect("localhost","root","root","python" )
vars = ('name1','name2','name3')
insert_values(db, vars)
db.close()
The cursor starts a transaction, so you don't want to re-use the same cursor for multiple updates unless they are part of an atomic transaction.

Categories