TypeError:'dict'对象不支持在此查询的第二个实例上抛出索引

Vin*_*llo 5 python sqlalchemy psycopg flask flask-sqlalchemy

所以我正在使用以下代码构建基于用户输入的查询:

    if empty_indic_form.validate_on_submit():
    query='select name, year, value, display_name from literal inner join ent on ent_id=ent.id where display_name in ('
    for i in indic_form.indicators.data:
        query=query+'\''+i+'\','
    query=query[:-1]+') '
    query=query+'and name in ('
    for c in order_c:
        query=query+c+','
    query=query[:-1]+')'
    data_return=db.engine.execute(query).fetchall()
Run Code Online (Sandbox Code Playgroud)

我已经确认查询看起来像它应该是什么,甚至有一个早期的会话,它返回一个像我期望的rowproxy对象列表.但是现在无论我做什么,我都会收到这个错误!

我已经将查询设置为模板中的变量,因此我可以将其打印出来,这是我得到的:

select name, year, value, display_name from literal inner join ent on ent_id=ent.id where display_name in ('Energy savings of primary energy (TJ)','Adolescent birth rate (women aged 15-19 years)','Net migration rate','Transmission and distribution losses (%)') and name in ('Burkina Faso', 'Ghana', 'Saudi Arabia', 'Pakistan')

我直接在Postgres DB上运行它,结果很棒.

在错误转储中,我注意到该data_return=db.engine.execute(query).fetchall()行正在构建一个空字典作为参数,这当然会在最后抛出该错误.我可以强迫它不要这样做吗?查询对象看起来像上面那样,现在它有什么问题?我应该在刷新页面或转到主页时杀死数据库会话吗?

Ilj*_*ilä 14

方法中的基本错误是使用字符串连接来构建SQL查询.如果indic_form.indicators.data或者order_c是用户提供的数据,例如来自HTTP请求,您可能已经为SQL注入打开了自己.错误

TypeError: 'dict' object does not support indexing
Run Code Online (Sandbox Code Playgroud)

这是串联的结果:您的查询字符串包含一个流氓"%",它是psycopg的占位符语法的一部分 - 通常与SQLAlchemy一起使用的DB-API与Postgresql交谈.这正是为什么不应该进行手动连接的原因.正确逃离可能很难.

IN使用Psycopg2中的元组自适应将值列表传递给运算符.在SQLAlchemy中,您将使用in_列操作符或1.2版引入扩展绑定参数.

不幸的是,在你的特殊情况下,SQLAlchemy包装,引擎,有一个问题:因为你的所有参数都是元组,SQLAlchemy引擎认为你试图将一个可迭代的参数元组作为multiparams传递给它并executemany()自动使用.您可以使用text()构造来解决此问题,该构造允许将DB-API不可知的绑定参数语法和字典作为参数容器:

from sqlalchemy import text

...

if empty_indic_form.validate_on_submit():
    # Note that the placeholders shouldn't have enclosing parentheses
    query = """SELECT name, year, value, display_name
               FROM literal
               INNER JOIN ent ON ent_id = ent.id
               WHERE display_name IN :display_name
                 AND name IN :name"""

    data_return = db.engine.execute(
        text(query), {'display_name': tuple(indic_form.indicators.data),
                      'name': tuple(order_c)}).fetchall()
Run Code Online (Sandbox Code Playgroud)

回顾一下:永远不要使用字符串连接或手动格式化来构建SQL查询.始终使用占位符/绑定参数.

  • 如该问题所示,这不是一个好主意.它只是字符串连接/格式化的另一种形式.如果用精巧的意思是使用`!r`,那么这既不安全也不实用:它会破坏包含```的字符串. (2认同)