简体   繁体   English

使用要获取的列名列表构建 psycopg2 查询

[英]Building a psycopg2 query using a list of the column names to fetch

A rather simple question but for which we surprisingly didn't found a solution.一个相当简单的问题,但令人惊讶的是我们没有找到解决方案。

Here is my current code, for executing a simple SQL query on a PostgreSQL database from Python 3.6.9 using psycopg2 ( '2.9.1 (dt dec pq3 ext lo64)' ):这是我当前的代码,用于使用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

We're more and more thinking that this is... not good .我们越来越认为这……不好 (awfully bad to be honest). (说实话非常糟糕)。

Therefore, we're trying to use a modern f-string notation:因此,我们尝试使用现代 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' );""")

But then, the query looks like:但是,查询看起来像:

SELECT  ('p.id', 'p.name', 'p.type', 'p.price', 'p.warehouse', 'p.location', ) FROM ...;

which is not valid in PSQL because 1. of the brackets, and 2. of the single quoted column names.这在 PSQL 中无效,因为 1. 的括号和 2. 的单引号列名。

We'd like to figure out a way to get rid of these.我们想找出一种方法来摆脱这些。

In between, we went back to the doc and remembered this:在此期间,我们回到文档并记住了这一点:

永远不要那样做!
https://www.psycopg.org/docs/usage.html https://www.psycopg.org/docs/usage.html

Ooops!哎呀! So we refactored it this way:所以我们这样重构它:

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()

and mogrify() says:mogrify()说:

"SELECT ('p.id', 'p.name', 'p.type', 'p.price', 'p.warehouse', 'p.location', ) FROM ...;"

here again, the brackets and the single quotes are causing troubles, but no error is actually raised.再次,括号和单引号引起了麻烦,但实际上并没有引发错误。
The only thing is that row evaluates to this strange result:唯一的事情是该row评估为这个奇怪的结果:

['('p.id', 'p.name', 'p.type', 'p.price', 'p.warehouse', 'p.location', )']

So, how could we cleverly and dynamically build a psycopg2 query using a list of parameters for column names without neglecting the security?那么,我们如何才能在不忽视安全性的情况下,使用列名参数列表巧妙地动态构建 psycopg2 查询呢?

(A trick could be to fetch all columns and filter them out after ... but there are too many columns, some with quiet large amount of data that we don't need, that's why we want to run a query using a precisely defined selection of columns, which may get dynamically extended by some function, otherwise we would have hard-coded these column names of course). (一个技巧可能是获取所有列并在之后将它们过滤掉……但是列太多了,有些列包含我们不需要的大量数据,这就是为什么我们要使用精确定义的来运行查询列的选择,这些列可能会被某个函数动态扩展,否则我们当然会对这些列名进行硬编码)。

OS: Ubuntu 18.04操作系统:Ubuntu 18.04
PostgreSQL: 13.3 (Debian 13.3-1.pgdg100+1) PostgreSQL:13.3(Debian 13.3-1.pgdg100+1)

The '%s' insertion will try to turn every argument into an SQL string, as @AdamKG pointed out.正如@AdamKG 指出的那样,'%s' 插入将尝试将每个参数转换为 SQL 字符串。 Instead, you can use the psycopg2.sql module will allow you to insert identifiers into queries, not just strings:相反,您可以使用psycopg2.sql模块将允许您在查询中插入标识符,而不仅仅是字符串:

from psycopg2 import sql

fields = ('id', 'name', 'type', 'price', 'warehouse', 'location', )

sql_query = sql.SQL(
          """SELECT {} 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.format(*[sql.Identifier(field) for field in fields]), (*fields, myid))
            row = curs.fetchone()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM