【发布时间】:2021-07-16 20:06:45
【问题描述】:
一个相当简单的问题,但令人惊讶的是我们没有找到解决方案。
这是我当前的代码,用于使用psycopg2 ('2.9.1 (dt dec pq3 ext lo64)') 从 Python 3.6.9 对 PostgreSQL 数据库执行简单的 SQL 查询:
import psycopg2
myid = 100
fields = ('p.id', 'p.name', 'p.type', 'p.price', 'p.warehouse', 'p.location', )
sql_query = ("SELECT " + ', '.join(fields) + " FROM product p "
"INNER JOIN owner o ON p.id = o.product_id "
"WHERE p.id = {} AND (o.dateof_purchase IS NOT NULL "
"OR o.state = 'checked_out' );"
).format(myid)
try:
with psycopg2.connect(**DB_PARAMS) as conn:
with conn.cursor(cursor_factory=DictCursor) as curs:
curs.execute(sql_query, )
row = curs.fetchone()
except psycopg2.Error as error:
raise ValueError(f"ERR: something went wrong with the query :\n{sql_query}") from None
我们越来越认为这是……不好。 (说实话太糟糕了)。
因此,我们尝试使用现代 f 字符串表示法:
sql_query = (f"""SELECT {fields} FROM product p
INNER JOIN owner o ON p.id = o.product_id
WHERE p.id = {myid} AND (o.dateof_purchase IS NOT NULL
OR o.state = 'checked_out' );""")
但是,查询看起来像:
SELECT ('p.id', 'p.name', 'p.type', 'p.price', 'p.warehouse', 'p.location', ) FROM ...;
这在 PSQL 中无效,因为 1. 中的括号和 2. 中的单引号列名。
我们想想办法摆脱这些。
在此期间,我们回到文档并记住了这一点:
https://www.psycopg.org/docs/usage.html
哎呀!所以我们这样重构它:
sql_query = (f"""SELECT %s FROM product p
INNER JOIN owner o ON p.id = o.product_id
WHERE p.id = %s AND (o.dateof_purchase IS NOT NULL
OR o.state = 'checked_out' );""")
try:
with psycopg2.connect(**DB_PARAMS) as conn:
with conn.cursor(cursor_factory=DictCursor) as curs:
# passing a tuple as it only accept one more argument after the query!
curs.execute(sql_query, (fields, myid))
row = curs.fetchone()
和mogrify() 说:
"SELECT ('p.id', 'p.name', 'p.type', 'p.price', 'p.warehouse', 'p.location', ) FROM ...;"
在这里,括号和单引号引起了麻烦,但实际上并没有引发错误。
唯一的问题是 row 计算出这个奇怪的结果:
['('p.id', 'p.name', 'p.type', 'p.price', 'p.warehouse', 'p.location', )']
那么,我们怎样才能巧妙地、动态地构建一个 psycopg2 查询,使用列名的参数列表而不忽略安全性?
(一个技巧可能是获取所有列并在之后将它们过滤掉...但是列太多了,有些包含我们不需要的大量数据,这就是为什么我们希望使用精确定义的列选择来运行查询,这些列可能会被某些函数动态扩展,否则我们当然会硬编码这些列名)。
操作系统:Ubuntu 18.04
PostgreSQL:13.3(Debian 13.3-1.pgdg100+1)
【问题讨论】:
-
使用成熟的 SQL 交互库,如 SQLAlchemy,它允许您动态构建查询的任何子句,包括 SELECT 列表。或者,回到原来的字符串操作。 not-even-at-gunpoint 警告是关于 variables - 通常是您针对行/单元格数据测试的用户提供的字符串。实际的表名、列名等(由您在代码中而非用户定义的内容)不能作为参数传递,您需要自己构建该字符串。无论您使用 f 字符串、连接还是其他任何正交的方法。
-
@AdamKG 在
psycopg2案例中是错误的。见sql。如果您不想要所有的包袱,那么 SQLAlchemy 是一个更好的选择。 -
感谢指点!我想我在 psycopg 上已经过时了——这看起来是一个非常好的和有用的补充。
标签: python postgresql psycopg2