【问题标题】:Sqlalchemy - how to get raw sql from insert(), update() statements with binded params?Sqlalchemy - 如何从带有绑定参数的 insert()、update() 语句中获取原始 sql?
【发布时间】:2015-09-08 14:54:37
【问题描述】:

例子:

from sqlalchemy.dialects import mysql
from sqlalchemy import Integer, Column, update, insert
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Test(Base):
    __tablename__ = "test"

    a = Column(Integer, primary_key=True)
    b = Column(Integer)


update_stmt = update(Test).where(Test.a == 1).values(b=2)
print update_stmt.compile(dialect=mysql.dialect(), compile_kwargs=  {"literal_binds": True})

insert_stmt = insert(Test).values(a=1, b=1)
print insert_stmt.compile(dialect=mysql.dialect())

这个结果是:

UPDATE test SET b=%s WHERE test.a = %s
INSERT INTO test (a, b) VALUES (%s, %s)

问题是如何让sqlalchemy像这样生成smth:

UPDATE test SET b=2 WHERE test.a = 1
INSERT INTO test (a, b) VALUES (1, 1)

对于selectcompile_kwargs= {"literal_binds": True} 可以解决问题,但对于updateinsert 不起作用。

感谢您的帮助。

附:我需要从 orm 构建原始 sql 查询,因此欢迎任何其他 orm 的建议,这些建议可以轻松生成原始 sql。

【问题讨论】:

标签: python orm sqlalchemy


【解决方案1】:

我刚刚在python 2.7SQLAlchemy (1.0.13) 上运行了这个sn-p,它成功了。

from sqlalchemy.dialects import mysql
from sqlalchemy import Integer, Column, update, insert
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


class Test(Base):
    __tablename__ = "test"

    a = Column(Integer, primary_key=True)
    b = Column(Integer)


update_stmt = update(Test).where(Test.a == 1).values(b=2)
print update_stmt.compile(dialect=mysql.dialect(), compile_kwargs={"literal_binds": True})

insert_stmt = insert(Test).values(a=1, b=1)
print insert_stmt.compile(dialect=mysql.dialect(), compile_kwargs={"literal_binds": True})

我的输出是:

UPDATE test SET b=2 WHERE test.a = 1
INSERT INTO test (a, b) VALUES (1, 1)

您的环境可能有问题?

【讨论】:

  • 它不适用于 ubuntu 16.04 xenial 附带的 slqlalchemy v1.0.11。
  • 一旦你使用 datetime.datetime 值,它就会失败:NotImplementedError: Don't know how to literal-quote value datetime.date(2016, 10, 3)
【解决方案2】:

使用 compile_kwargs 的解决方案——正如在别处所宣传的——仅部分有效,即对于简单的数据类型,如 Integer 或 String(从 SQLAlchemy v1.1.6 开始)。

+-------------+----------------+--------+---------------+----------+
| SQA version | compile_kwargs | SELECT | INSERT/UPDATE | datetime |
+=============+================+========+===============+==========+
| 0.7.9       |       --       |    --  |        --     |    --    |
+-------------+----------------+--------+---------------+----------+
| 0.9.4       |       √        |   1/2  |        --     |    --    |
+-------------+----------------+--------+---------------+----------+
| 1.0.11      |       √        |    √   |        --     |    --    |
+-------------+----------------+--------+---------------+----------+
| 1.0.13      |       √        |    √   |       1/2     |    --    |
+-------------+----------------+--------+---------------+----------+
| 1.1.6       |       √        |    √   |       1/2     |    --    |
+-------------+----------------+--------+---------------+----------+

根据要求和限制,以下解决方案可以胜任:

  • 如果可以使用SQLAlchemyv1.0.13或以上版本,this answer提供了最简单的解决方案。 LiteralDialect 类仍然需要提供对数据类型(如日期时间)的正确引用。虽然不完整,但添加缺失的数据类型非常简单。不幸的是,您必须将 None 修复为 null() 以进行 INSERT/UPDATE。

  • 如果您可以使用 SQLAlchemy v1.0.11 并且只需要 SELECT 语句(不是 INSERT/UPDATE),您还可以使用来自the above answer 的解决方案。 SQLAlchemy v1.0.11 随当前 LTS 版本(截至 2017 年 3 月)的 ubuntu 16.04 xenial 一起分发。

如果您被一些旧版本的 SQLAlchemy 卡住,那么就没有甜蜜和简单的方法来解决这个问题。

所以我想出了some code supporting SQL compilation 的查询 (SELECT) 以及 INSERT 和 UPDATE 语句。该代码适用于 SQLAlchemy v0.7.9 - v1.1.6 和 Python2/Python3。

detailed (and hilarious) analysis of the various features 包含在 doctest 中。

相关的代码部分是:

【讨论】:

  • 伟大的工作。不幸的是,我目前不确定如何为更多数据类型实现文字引用。在我的情况下,我在尝试文字化语句(针对 MySQL 数据库)时遇到了 TypeError,其中包含引用具有 enum 数据类型的列的 where 子句:__init__() got an unexpected keyword参数 '_enums' 是相应的消息。我想我必须为这种类型定义一个到文字值的转换,但我不知道如何。
  • @le_affan SQLAlchemy v1.2.9 引入了一个新的关键字参数 _enums,这会导致 Enum 的 StringLiteral 中的 TypeError。我在上面的同一站点上准备了一个新版本 0.2。
【解决方案3】:

这不像上面那样完整的答案。我会推荐 wolfmanx 和其他人链接的方法。

我使用了适合我用例的快捷方式。如果由于某些不受支持的类型(如 datetime 或 UUID)而导致使用文字绑定编译失败,则可以手动将值转换为字符串。

我刚刚使用了暴露绑定的.compile 方法。

try:
    sql = str(query.compile(dialect=postgresql.dialect(), compile_kwargs={'literal_binds': True}))
except NotImplementedError:
    # This has special types which cannot be converted to string automatically.
    compiler = query.compile()
    variables = {key: str(value.value) for key, value in compiler.binds.items() if not key.startswith('%')}
    sql = str(query)
    # Manually replace the variables in the sql statement with their values.
    # This won't perfectly match the original statement, as this converts everything to str and wraps in double-quotes for simplicity.
    for variable, value in variables.items():
        # Variables are prefixed with :
        # Example: "DELETE FROM table WHERE skill.id = :id_1"
        sql = sql.replace(f':{variable}', f'"{value}"')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-22
    • 2014-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-27
    相关资源
    最近更新 更多