自定义 SQL 构造和编译扩展
提供用于创建自定义子句和编译器的API。
简介
使用涉及创建一个或多个 ClauseElement
子类和定义其编译的一个或多个可调用文件:
from sqlalchemy.ext.compiler import compiles from sqlalchemy.sql.expression import ColumnClause class MyColumn(ColumnClause): pass @compiles(MyColumn) def compile_mycolumn(element, compiler, **kw): return "[%s]" % element.name
上面, MyColumn
延伸 ColumnClause
,命名列对象的基表达式元素。这个 compiles
装饰器将自己注册到 MyColumn
类,以便在对象编译为字符串时调用它::
from sqlalchemy import select s = select(MyColumn('x'), MyColumn('y')) print(str(s))
生产::
SELECT [x], [y]
方言特定编译规则
编译器也可以指定方言。将为使用中的方言调用适当的编译器::
from sqlalchemy.schema import DDLElement class AlterColumn(DDLElement): def __init__(self, column, cmd): self.column = column self.cmd = cmd @compiles(AlterColumn) def visit_alter_column(element, compiler, **kw): return "ALTER COLUMN %s ..." % element.column.name @compiles(AlterColumn, 'postgresql') def visit_alter_column(element, compiler, **kw): return "ALTER TABLE %s ALTER COLUMN %s ..." % (element.table.name, element.column.name)
第二 visit_alter_table
将在任何时候调用 postgresql
使用方言。
编译自定义表达式构造的子元素
这个 compiler
论证是 Compiled
使用中的对象。可以检查此对象以获取有关正在编译的任何信息,包括 compiler.dialect
, compiler.statement
等等 SQLCompiler
和 DDLCompiler
两者都包括 process()
可用于编译嵌入属性的方法:
from sqlalchemy.sql.expression import Executable, ClauseElement class InsertFromSelect(Executable, ClauseElement): def __init__(self, table, select): self.table = table self.select = select @compiles(InsertFromSelect) def visit_insert_from_select(element, compiler, **kw): return "INSERT INTO %s (%s)" % ( compiler.process(element.table, asfrom=True, **kw), compiler.process(element.select, **kw) ) insert = InsertFromSelect(t1, select(t1).where(t1.c.x>5)) print(insert)
生产::
"INSERT INTO mytable (SELECT mytable.x, mytable.y, mytable.z FROM mytable WHERE mytable.x > :x_1)"
注解
以上 InsertFromSelect
构造只是一个示例,使用 Insert.from_select()
方法。
注解
以上 InsertFromSelect
构造可能希望启用“自动提交”。见 在构造上启用自动提交 对于这个步骤。
SQL和DDL编译器之间的交叉编译
SQL和DDL构造都是使用不同的基本编译器编译的。- SQLCompiler
和 DDLCompiler
. 通常需要从DDL表达式中访问SQL表达式的编译规则。这个 DDLCompiler
包括访问器 sql_compiler
因此,例如下面我们在其中生成一个嵌入SQL表达式的检查约束:
@compiles(MyConstraint) def compile_my_constraint(constraint, ddlcompiler, **kw): kw['literal_binds'] = True return "CONSTRAINT %s CHECK (%s)" % ( constraint.name, ddlcompiler.sql_compiler.process( constraint.expression, **kw) )
上面,我们向流程步骤添加了一个额外的标志,由调用 SQLCompiler.process()
,这就是 literal_binds
旗帜。这表示任何引用 BindParameter
对象或其他“文本”对象,如引用字符串或整数的对象,应呈现 in-place ,而不是被称为绑定参数;在发出DDL时,通常不支持绑定参数。
在构造上启用自动提交
从本节召回 库级(例如仿真)自动提交 那 Engine
当在没有用户定义的事务的情况下被要求执行一个构造时,检测给定的构造是否表示DML或DDL,即数据修改或数据定义语句,它要求(或可能要求,在DDL的情况下)提交由DBAPI生成的事务(请记住,DBAPI总是有一个事务G不管SQLAlchemy做什么,都要继续。检查这一点实际上是通过检查构造上的“autocommit”执行选项来完成的。当构建一个像insert derivation、一个新的ddl类型或者可能是一个改变数据的存储过程这样的构造时,需要设置“autocommit”选项,以便语句以“无连接”执行方式运行(如中所述 无连接执行,隐式执行 )
目前,一种快速的方法是子类 Executable
,然后将“自动提交”标志添加到 _execution_options
字典(注:这是一个“冻结”的字典,提供了一个生成 union()
方法:
from sqlalchemy.sql.expression import Executable, ClauseElement class MyInsertThing(Executable, ClauseElement): _execution_options = \ Executable._execution_options.union({'autocommit': True})
更简洁地说,如果构造确实类似于插入、更新或删除, UpdateBase
可以使用,它已经是 Executable
, ClauseElement
包括 autocommit
旗帜:
from sqlalchemy.sql.expression import UpdateBase class MyInsertThing(UpdateBase): def __init__(self, ...): ...
子类的DDL元素 DDLElement
已打开“自动提交”标志。
更改现有构造的默认编译
编译器扩展也适用于现有的构造。当重写内置SQL构造的编译时,@compiles修饰符将在适当的类上调用(请确保使用该类,即 Insert
或 Select
,而不是创建函数,例如 insert()
或 select()
)
在新的编译函数中,要获得“原始”编译例程,请使用适当的visit_xxx方法-这是因为compiler.process()将调用重写例程并导致无休止的循环。例如,要将“prefix”添加到所有insert语句中:
from sqlalchemy.sql.expression import Insert @compiles(Insert) def prefix_inserts(insert, compiler, **kw): return compiler.visit_insert(insert.prefix_with("some prefix"), **kw)
上面的编译器在编译时会在所有insert语句前加上“some prefix”。
更改类型的编译
compiler
works for types, too, such as below where we implement the MS-SQL specific 'max' keyword for String
/VARCHAR
::
@compiles(String, 'mssql') @compiles(VARCHAR, 'mssql') def compile_varchar(element, compiler, **kw): if element.length == 'max': return "VARCHAR('max')" else: return compiler.visit_VARCHAR(element, **kw) foo = Table('foo', metadata, Column('data', VARCHAR('max')) )
子类化准则
使用编译器扩展的很大一部分是对SQLAlchemy表达式构造进行子类化。为了使这更容易,表达式和模式包提供了一组“基础”,用于公共任务。概要如下:
ClauseElement
-这是根表达式类。任何SQL表达式都可以从此基中派生,对于较长的构造(如专用的insert语句),它可能是最佳选择。ColumnElement
-所有“类列”元素的根。在select语句的“columns”子句中放置的任何内容(以及order by和group by)都可以从此派生-对象将自动具有python的“comparison”行为。ColumnElement
班级希望有一个type
是表达式的返回类型的成员。这可以在构造函数的实例级别上建立,或者在类级别上建立,如果它的一般常量为:class timestamp(ColumnElement): type = TIMESTAMP()
FunctionElement
-这是一个混合的ColumnElement
和“FROM子句”类似,表示SQL函数或调用的存储过程类型。因为大多数数据库都支持“select from<some function>”行中的语句。FunctionElement
添加要在的From子句中使用的功能select()
结构:from sqlalchemy.sql.expression import FunctionElement class coalesce(FunctionElement): name = 'coalesce' @compiles(coalesce) def compile(element, compiler, **kw): return "coalesce(%s)" % compiler.process(element.clauses, **kw) @compiles(coalesce, 'oracle') def compile(element, compiler, **kw): if len(element.clauses) > 2: raise TypeError("coalesce only supports two arguments on Oracle") return "nvl(%s)" % compiler.process(element.clauses, **kw)
DDLElement
-所有DDL表达式的根,如create table、alter table等编译DDLElement
子类由DDLCompiler
而不是SQLCompiler
.DDLElement
还特点Table
和MetaData
事件挂钩通过execute_at()
方法,允许在创建表和删除表序列期间调用构造。Executable
-这是一个mixin,应该与表示“独立”SQL语句的任何表达式类一起使用,该语句可以直接传递给execute()
方法。它已经隐含在DDLElement
和FunctionElement
.
其他示例
“UTC时间戳”函数
一个类似于“当前时间戳”的函数,只应用适当的转换,使时间以UTC时间为单位。时间戳最好以UTC的形式存储在关系数据库中,没有时区。因此,您的数据库不会认为时间在夏时制结束的时间内倒转,没有时区,因为时区类似于字符编码-它们最好只应用于应用程序的端点(即,在用户输入时转换为UTC,在显示时重新应用所需时区)。
对于PostgreSQL和Microsoft SQL Server::
from sqlalchemy.sql import expression from sqlalchemy.ext.compiler import compiles from sqlalchemy.types import DateTime class utcnow(expression.FunctionElement): type = DateTime() @compiles(utcnow, 'postgresql') def pg_utcnow(element, compiler, **kw): return "TIMEZONE('utc', CURRENT_TIMESTAMP)" @compiles(utcnow, 'mssql') def ms_utcnow(element, compiler, **kw): return "GETUTCDATE()"
示例用法:
from sqlalchemy import ( Table, Column, Integer, String, DateTime, MetaData ) metadata = MetaData() event = Table("event", metadata, Column("id", Integer, primary_key=True), Column("description", String(50), nullable=False), Column("timestamp", DateTime, server_default=utcnow()) )
“最大”功能
“最大的”函数被赋予任意数量的参数,并返回一个值最高的参数-相当于python的 max
功能。与仅包含两个参数的基于案例的版本相比,SQL标准版本:
from sqlalchemy.sql import expression, case from sqlalchemy.ext.compiler import compiles from sqlalchemy.types import Numeric class greatest(expression.FunctionElement): type = Numeric() name = 'greatest' @compiles(greatest) def default_greatest(element, compiler, **kw): return compiler.visit_function(element) @compiles(greatest, 'sqlite') @compiles(greatest, 'mssql') @compiles(greatest, 'oracle') def case_greatest(element, compiler, **kw): arg1, arg2 = list(element.clauses) return compiler.process(case([(arg1 > arg2, arg1)], else_=arg2), **kw)
示例用法:
Session.query(Account).\ filter( greatest( Account.checking_balance, Account.savings_balance) > 10000 )
“假”表达
呈现“假”常量表达式,在没有“假”常量的平台上呈现为“0”::
from sqlalchemy.sql import expression from sqlalchemy.ext.compiler import compiles class sql_false(expression.ColumnElement): pass @compiles(sql_false) def default_false(element, compiler, **kw): return "false" @compiles(sql_false, 'mssql') @compiles(sql_false, 'mysql') @compiles(sql_false, 'oracle') def int_false(element, compiler, **kw): return "0"
示例用法:
from sqlalchemy import select, union_all exp = union_all( select(users.c.name, sql_false().label("enrolled")), select(customers.c.name, customers.c.enrolled) )
Object Name | Description |
---|---|
compiles(class_, *specs) | 将函数注册为给定函数的编译器 |
deregister(class_) | 删除与给定的 |
- function sqlalchemy.ext.compiler.compiles(class_, *specs)¶
将函数注册为给定函数的编译器
ClauseElement
类型。
- function sqlalchemy.ext.compiler.deregister(class_)¶
删除与给定的
ClauseElement
类型。