SQLAlchemy核心语句对象或表达式片段以及ORM的“字符串化”。 Query
对象,在大多数简单的情况下,使用 str()
内置函数,如下所示 print
函数(注意python print
函数也调用 str()
如果不明确使用,则自动)::
>>> from sqlalchemy import table, column, select
>>> t = table('my_table', column('x'))
>>> statement = select([t])
>>> print(str(statement))
SELECT my_table.x
FROM my_table
这个 str()
可以在ORM上调用builtin或等效函数 Query
对象以及任何语句,如 select()
, insert()
以及任何表达式片段,例如:
>>> from sqlalchemy import column
>>> print(column('x') == 'some value')
x = :x_1
当我们正在串接的语句或片段包含具有数据库特定字符串格式的元素时,或者当它包含仅在某类数据库中可用的元素时,就会出现复杂的情况。在这些情况下,我们可能会得到一个字符串化语句,该语句对于我们要定位的数据库语法不正确,或者操作可能会引发 UnsupportedCompilationError
例外。在这些情况下,有必要使用 ClauseElement.compile()
方法,同时传递 Engine
或 Dialect
表示目标数据库的对象。如下图所示,如果我们有一个MySQL数据库引擎,我们可以用MySQL方言将语句串接起来:
from sqlalchemy import create_engine
engine = create_engine("mysql+pymysql://scott:tiger@localhost/test")
print(statement.compile(engine))
更直接,不需要建立 Engine
对象我们可以实例化 Dialect
对象,如下所示,我们使用PostgreSQL方言:
from sqlalchemy.dialects import postgresql
print(statement.compile(dialect=postgresql.dialect()))
提供ORM时 Query
对象,以便 ClauseElement.compile()
方法我们只需要访问 statement
先访问:
statement = query.statement
print(statement.compile(someengine))
警告
从未 对从不受信任的输入(如Web窗体或其他用户输入应用程序)接收的字符串内容使用此技术。sqlAlchemy将python值强制为直接sql字符串值的功能有 对不受信任的输入不安全,也不验证正在传递的数据类型 . 对关系数据库以编程方式调用非DDL SQL语句时,始终使用绑定参数。
上述表单将在传递给python时呈现SQL语句。 DBAPI ,其中包括绑定参数不会以内联方式呈现。sqlAlchemy通常不将绑定参数串接,因为这是由python dbapi适当处理的,更不用说绕过绑定参数可能是现代Web应用程序中最广泛利用的安全漏洞。在某些情况下(如发出DDL的情况下),SQLAlchemy执行此字符串化的能力有限。为了访问此功能,可以使用 literal_binds
标志,传递给 compile_kwargs
::
from sqlalchemy.sql import table, column, select
t = table('t', column('x'))
s = select([t]).where(t.c.x == 5)
print(s.compile(compile_kwargs={"literal_binds": True})) # **do not use** with untrusted input!!!
上面的方法有一个警告,即它只支持基本类型,如int和string,而且如果 bindparam()
如果不直接使用预设值,它也无法将其串接起来。
要支持不支持的类型的内联文本呈现,请实现 TypeDecorator
对于包含 TypeDecorator.process_literal_param()
方法:
from sqlalchemy import TypeDecorator, Integer
class MyFancyType(TypeDecorator):
impl = Integer
def process_literal_param(self, value, dialect):
return "my_fancy_formatting(%s)" % value
from sqlalchemy import Table, Column, MetaData
tab = Table('mytable', MetaData(), Column('x', MyFancyType()))
print(
tab.select().where(tab.c.x > 5).compile(
compile_kwargs={"literal_binds": True})
)
生产产量如下:
SELECT mytable.x
FROM mytable
WHERE mytable.x > my_fancy_formatting(5)
这个 Operators.op()
方法允许创建一个自定义数据库运算符,否则sqlAlchemy不知道:
>>> print(column('q').op('->')(column('p')))
q -> p
但是,当在复合表达式的右侧使用它时,它不会像我们预期的那样生成括号::
>>> print((column('q1') + column('q2')).op('->')(column('p')))
q1 + q2 -> p
在上面的什么地方,我们可能想要 (q1 + q2) -> p
.
这种情况的解决方案是使用 Operators.op.precedence
参数设置为高位,其中100是最大值,任何sqlAlchemy运算符使用的最大值当前为15::
>>> print((column('q1') + column('q2')).op('->', precedence=100)(column('p')))
(q1 + q2) -> p
我们还可以使用 ColumnElement.self_group()
方法:
>>> print((column('q1') + column('q2')).self_group().op('->')(column('p')))
(q1 + q2) -> p
许多数据库都会在括号过多或括号位于不希望出现的异常位置时出错,因此SQLAlchemy不会基于分组生成括号,它使用运算符优先级,并且如果已知运算符是关联的,则括号生成的最少。否则,表达式如下:
column('a') & column('b') & column('c') & column('d')
将产生:
(((a AND b) AND c) AND d)
这很好,但可能会惹恼人们(并被报告为一个bug)。在其他情况下,它会导致更容易混淆数据库或至少具有可读性的事情,例如:
column('q', ARRAY(Integer, dimensions=2))[5][6]
将产生:
((q[5])[6])
也有一些边缘案例,我们可以得到 "(x) = 7"
数据库也不喜欢这样。所以括号并不是简单的括号,它使用操作符优先级和关联性来确定分组。
为了 Operators.op()
,优先级值默认为零。
如果我们违约了 Operators.op.precedence
到100,例如最高?然后这个表达式产生更多的圆括号,但如果不是这样就可以了,也就是说,这两个圆括号等价:
>>> print (column('q') - column('y')).op('+', precedence=100)(column('z'))
(q - y) + z
>>> print (column('q') - column('y')).op('+')(column('z'))
q - y + z
但这两个不是:
>>> print column('q') - column('y').op('+', precedence=100)(column('z'))
q - y + z
>>> print column('q') - column('y').op('+')(column('z'))
q - (y + z)
目前,还不清楚,只要我们基于运算符优先级和关联性进行括号化,是否真的有一种方法可以自动为没有给定优先级的泛型运算符进行括号化,这在所有情况下都适用,因为有时您希望自定义运算符的优先级低于其他运算符有时你希望它更高。
如果上面的“binary”表达式强制使用 self_group()
方法时 op()
假定左侧的复合表达式总是可以无害地加括号。也许这种改变可以在某个时刻进行,但是目前保持括号规则在内部更一致似乎是更安全的方法。