Parameterized queries with psycopg2 / Python DB-API and PostgreSQL
psycopg2
follows the rules for DB-API 2.0 (set down in PEP-249). That means you can call execute
method from your cursor
object and use the pyformat
binding style, and it will do the escaping for you. For example, the following should be safe (and work):
cursor.execute("SELECT * FROM student WHERE last_name = %(lname)s",
{"lname": "Robert'); DROP TABLE students;--"})
make parameterized SQL select statement with psycopg2 in python
First this is a duplicate of your previous question Previous. You should have just continued the discussion there. As I stated there it is possible to do what you using the sql module from psycopg2. As example from one of my applications:
class NotificationReport():
"""Builds a query for finding task notifications.
Use form_choices passed in to modify the select query for task
notifications using psycopg2.sql module. Filter on status which is some
combination of notify_expired and notify_cancelled.
"""
def __init__(self, form_choices):
self.id = "notification_report"
self.form_choices = form_choices
def returnQuery(self):
flds, defaults = data.fetchFields(data.TaskNotification)
base_sql = sql.SQL("""SELECT
task_title, {}
FROM
tasks
JOIN
task_priority AS tp
ON
tasks. task_priority_fk= tp.priority_id
JOIN
task_type AS tt
ON
tasks.task_type_fk = tt.task_type_id
LEFT JOIN
task_notification AS tn
ON
tasks.task_id = tn.task_id_fk
""").format(sql.SQL(",").join(map(sql.Identifier, flds)))
f_choices = self.form_choices
and_sql = None
ops_list = []
if f_choices:
for choice in f_choices:
if choice.get("status"):
status = choice["status"]
status_dict = {"open": ("notify_expired = 'f' "),
"expired": ("notify_expired = 't' "),
}
if status == "all":
pass
else:
ops = sql.SQL(status_dict[status])
ops_list.append(ops)
if ops_list:
and_sql = sql.Composed([base_sql, sql.SQL(" AND ")])
additional_and = sql.SQL(" AND ").join(ops_list)
ops_sql = sql.Composed([and_sql, additional_and])
orderby_sql = sql.SQL("""ORDER BY
task_title""")
if and_sql:
combined_sql = sql.Composed([ops_sql, orderby_sql])
else:
combined_sql = sql.Composed([base_sql, orderby_sql])
return combined_sql
Output. First no parameters supplied to report:
SELECT
task_title, "task_id_fk","before_value","before_interval","every_value","every_interval","notify_note","notify_id","notify_expired"
FROM
tasks
JOIN
task_priority AS tp
ON
tasks. task_priority_fk= tp.priority_id
JOIN
task_type AS tt
ON
tasks.task_type_fk = tt.task_type_id
LEFT JOIN
task_notification AS tn
ON
tasks.task_id = tn.task_id_fk
ORDER BY
task_title
Then with status:
SELECT
task_title, "task_id_fk","before_value","before_interval","every_value","every_interval","notify_note","notify_id","notify_expired"
FROM
tasks
JOIN
task_priority AS tp
ON
tasks. task_priority_fk= tp.priority_id
JOIN
task_type AS tt
ON
tasks.task_type_fk = tt.task_type_id
LEFT JOIN
task_notification AS tn
ON
tasks.task_id = tn.task_id_fk
AND notify_expired = 'f' ORDER BY
task_title
Psycopg2 parameterized execute query
You might want to use a multiline string to define your SQL query, to provide more meaningful names you can use a dictionary to pass values to psycopg2.execute():
import psycopg2
conn = psycopg2.connect("dbname=mf port=5959 host=localhost user=mf_usr")
cur = conn.cursor()
sql = """
SELECT
id
FROM tbl_users
WHERE
b_enabled = %(enabled)s
"""
print (cur.mogrify(sql, {'enabled': "True"}).decode('utf-8'))
# cur.execute(sql, {'enabled': "True"})
Output:
SELECT
id
FROM tbl_users
WHERE
b_enabled = 'True'
Please have a look at the official docs for further information.
any way to make parameterized queries and encapsulate it python in function
If you want to pass named arguments to cursor.execute()
, you can use the %(name)s
syntax and pass in a dict. See the documentation for more details.
Here is an example using your query:
import datetime
import psycopg2
EXAMPLE_QUERY = """
SELECT
date_trunc('week', date_received) AS received_week,
cl_val,
ROUND(ROUND(SUM(quant_received * standard_price)::numeric,4) / SUM(quant_received),4) AS mk_price_1,
ROUND(ROUND(SUM(quant_received * mg_fb_price)::numeric,4) / SUM(quant_received),4) AS mg_price_1,
ROUND(ROUND(SUM(quant_received * mk_price_variance)::numeric,4) / SUM(quant_received),4) AS fb_mk_price_var,
ROUND(ROUND(SUM(quant_received * freight)::numeric,4) / SUM(quant_received),4) AS freight_new,
ROUND(ROUND(SUM(quant_received * grd_delv_cost)::numeric,4) / SUM(quant_received),4) AS grd_delv_cost_new,
TO_CHAR(SUM(quant_received), '999G999G990D') AS Volume_Received
FROM mytable
WHERE date_received >= %(min_date_received)s
AND date_received <= %(max_date_received)s
AND item_type = %(item_type)s
AND cl_val IN %(cl_vals)s
AND order_type IN %(order_types)s
AND pk_name IN %(pk_names)s
AND pk_est NOT IN %(pk_ests)s
GROUP BY received_week ,cl_val
ORDER BY received_week ASC, cl_val ASC;
"""
def execute_example_query(cursor, **kwargs):
"""Execute the example query with given parameters."""
cursor.execute(EXAMPLE_QUERY, kwargs)
return cursor.fetchall()
if __name__ == '__main__':
connection = psycopg2.connect(database="myDB", user="postgres", password="passw", host="localhost", port=5432)
cursor = connection.cursor()
execute_example_query(
cursor,
min_date_received = datetime.date(2010, 10, 1),
max_date_received = datetime.date(2012, 12, 31),
item_type = 'processed',
cl_vals = ('12.5', '6.5', '8.1', '8.5', '9.0'),
order_types = ('formula',),
pk_names = ('target', 'costco', 'AFG', 'KFC'),
pk_ests = ('12',)
)
id variable in postgres
In Python's Postgres library, you can interpolate values with %s
and pass them as secondary arguments to execute
.
my_cursor.execute("SELECT word FROM words WHERE id = %s", (my_id,))
Passing list of parameters to SQL in psycopg2
Python tuples are converted to sql lists in psycopg2:
cur.mogrify("SELECT * FROM table WHERE column IN %s;", ((1,2,3),))
would output
'SELECT * FROM table WHERE column IN (1,2,3);'
For Python newcomers: It is unfortunately important to use a tuple, not a list here. Here's a second example:
cur.mogrify("SELECT * FROM table WHERE column IN %s;",
tuple([row[0] for row in rows]))
Unable to Pass in Table Name for Query psycopg2
According to:
Passing table name as a parameter in psycopg2
You should be using this
template:
from psycopg2 import sql
cur.execute(
sql.SQL("insert into {table} values (%s, %s)")
.format(table=sql.Identifier('my_table')), # table name here
[10, 20]) ## other parameters here
Related Topics
Force Python to Forego Native SQLite3 and Use the (Installed) Latest SQLite3 Version
How to Use Selenium to Automate Chase Site Login
Generating HTML Documents in Python
What Is a "Good" Palette for Divergent Colors in R? (Or: Can Viridis and Magma Be Combined Together)
Is There a Static Analysis Tool for Python, Ruby, SQL, Cobol, Perl, and Pl/Sql
Does Ruby Have Something Like Python's List Comprehensions
How to Validate a Date String Format in Python
Blocking and Non Blocking Subprocess Calls
Where Is a Complete Example of Logging.Config.Dictconfig
Running Windows Shell Commands with Python
Why Is Variable1 += Variable2 Much Faster Than Variable1 = Variable1 + Variable2
How to Write a Perl, Python, or Ruby Program to Change the Memory of Another Process on Windows
Programmatically Extract Data from an Excel Spreadsheet