如何从SQLAlchemy表达式获取原始编译的SQL查询?
我有一个 SQLAlchemy 的查询对象,想要获取编译后的 SQL 语句的文本,并且希望所有的参数都已经绑定好(也就是说,不想看到像 %s
这样的占位符,或者其他等待被绑定的变量)。
调用 str()
方法后,得到的结果大概是这样的:
SELECT id WHERE date_added <= %s AND date_added >= %s ORDER BY count DESC
我试着查看 query._params,但它是一个空字典。我还根据 这个关于 sqlalchemy.ext.compiler.compiles
装饰器的例子 写了自己的编译器,但即使在那个例子中,语句里还是有 %s
,而我想要的是数据。
我有点搞不清楚我的参数是什么时候混入的,形成查询;在检查查询对象时,它们总是一个空字典(不过查询执行得很好,当你打开回显日志时,数据库引擎会打印出来)。
我开始觉得 SQLAlchemy 不想让我知道底层的查询,因为这会破坏表达式 API 接口与不同数据库 API 之间的一般性质。我并不介意查询在我知道之前就被执行;我只是想知道!
13 个回答
这个应该适用于 Sqlalchemy 版本大于等于 0.6 的情况。
from sqlalchemy.sql import compiler
from psycopg2.extensions import adapt as sqlescape
# or use the appropiate escape function from your db driver
def compile_query(query):
dialect = query.session.bind.dialect
statement = query.statement
comp = compiler.SQLCompiler(dialect, statement)
comp.compile()
enc = dialect.encoding
params = {}
for k,v in comp.params.iteritems():
if isinstance(v, unicode):
v = v.encode(enc)
params[k] = sqlescape(v)
return (comp.string.encode(enc) % params).decode(enc)
这段文档提到,literal_binds
可以用来打印一个查询q
,包括它的参数:
print(q.statement.compile(compile_kwargs={"literal_binds": True}))
不过,这种方法有一些限制,它只支持基本类型,比如整数和字符串。而且,如果直接使用一个没有预设值的
bindparam()
,也无法将其转换成字符串。
文档还给出了这样的警告:
绝对不要对来自不可信输入的字符串内容使用这种技术,比如从网页表单或其他用户输入的应用程序中获取的内容。SQLAlchemy将Python值转换为直接SQL字符串的功能对于不可信输入并不安全,也不会验证传递数据的类型。在对关系数据库执行非DDL的SQL语句时,始终要使用绑定参数。
Nicolas Cadou的这篇博客提供了一个更新的答案。
从这篇博客中引用,这个方法被推荐并且对我有效:
from sqlalchemy.dialects import postgresql print str(q.statement.compile(dialect=postgresql.dialect()))
其中 q 被定义为:
q = DBSession.query(model.Name).distinct(model.Name.value) \ .order_by(model.Name.value)
或者任何类型的 session.query()
。