简体   繁体   English

Sqlalchemy - 如何从带有绑定参数的 insert()、update() 语句中获取原始 sql?

[英]Sqlalchemy - how to get raw sql from insert(), update() statements with binded params?

Example:例子:

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

Result of this is:这样做的结果是:

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

The question is how to make sqlalchemy generate smth like this:问题是如何让 sqlalchemy 像这样生成 smth:

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

For select , compile_kwargs= {"literal_binds": True} solves the issue, but it doesn't work for update , insert .对于selectcompile_kwargs= {"literal_binds": True}解决了这个问题,但它不适用于updateinsert

Thanks for any help.谢谢你的帮助。

PS I need to build raw sql queries from orm, so any suggestions of any other orms, that have easy way to generate raw sql, are welcome. PS 我需要从 orm 构建原始 sql 查询,因此欢迎任何其他 orms 的建议,这些建议可以轻松生成原始 sql。

The solution using compile_kwargs -- as advertised elsewhere -- works only partially, ie for simple data types like Integer or String (as of SQLAlchemy v1.1.6). 使用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     |    --    |
+-------------+----------------+--------+---------------+----------+

Depending on requirements and restrictions, the following solutions will do the job: 根据要求和限制,以下解决方案将完成工作:

  • If you can use SQLAlchemy v1.0.13 or above, the simplest solution is provided in this answer . 如果您可以使用SQLAlchemy v1.0.13或更高版本,则此答案中提供了最简单的解决方案。 The LiteralDialect class presented there is still needed to provide correct quoting of data types like datetime. 提供的LiteralDialect类仍然需要提供正确的数据类型引用,如datetime。 Although it is not complete, adding missing data types is very straightforward. 虽然它不完整,但添加缺少的数据类型非常简单。 And unfortunately you have to fix None to null() for INSERT/UPDATE. 不幸的是,你必须为INSERT / UPDATE修复None to null()

  • If you can use SQLAlchemy v1.0.11 and need only SELECT statements ( not INSERT/UPDATE ), you can also use the solution from the above answer . 如果您可以使用SQLAlchemy v1.0.11并且需要SELECT语句( 不是INSERT / UPDATE ),那么您也可以使用上面答案中的解决方案。 SQLAlchemy v1.0.11 is distributed with ubuntu 16.04 xenial, the current LTS version (as of Mar 2017). SQLAlchemy v1.0.11与ubuntu 16.04 xenial一起发布,这是当前的LTS版本(截至2017年3月)。

If you are stuck with some older version of SQLAlchemy, there is no sweet and simple approach to the problem. 如果您遇到一些旧版本的SQLAlchemy,那么问题就没有简单易用的方法。

So I came up with some code supporting SQL compilation of queries (SELECT) as well as INSERT and UPDATE statements. 所以我提出了一些支持SQL编译查询(SELECT)以及INSERT和UPDATE语句的代码。 The code works with SQLAlchemy v0.7.9 - v1.1.6 and Python2/Python3. 该代码适用于SQLAlchemy v0.7.9 - v1.1.6和Python2 / Python3。

A detailed (and hilarious) analysis of the various features is included as doctest. 各种功能详细(和热闹)分析包含在doctest中。

The relevant code sections are: 相关的代码部分是:

I just run this snippet on python 2.7 and SQLAlchemy (1.0.13) and it worked. 我只是在python 2.7SQLAlchemy (1.0.13)上运行这个片段并且它有效。

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

my output is: 我的输出是:

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

Maybe something is wrong with your environment? 也许您的环境出了问题?

This isn't as complete of an answer as above.这不像上面那样完整。 I would recommend the approach linked by wolfmanx and others.我会推荐由 wolfmanx 和其他人链接的方法。

I used this shortcut which fit my use-case.我使用了适合我的用例的快捷方式。 If it fails compiling with literal binds due to some unsupported type like datetime or UUID, then manually converting the values to strings works.如果由于某些不受支持的类型(如日期时间或 UUID)而无法使用文字绑定编译,则手动将值转换为字符串。

I just used the .compile method which exposed the binds.我只是使用了暴露绑定的.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}"')

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

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