SqlAlchemy 2.0 中文文档(三十七)(1)https://developer.aliyun.com/article/1562696
更多示例
“UTC 时间戳”函数
一个类似于 “CURRENT_TIMESTAMP” 的函数,但应用适当的转换,使时间为 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() inherit_cache = True @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()) )
“GREATEST”函数
“GREATEST”函数接受任意数量的参数,并返回具有最高值的参数 - 它等同于 Python 的 max
函数。与仅容纳两个参数的基于 CASE 的版本相比,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' inherit_cache = True @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 )
“false” 表达式
渲染“false”常量表达式,对于没有“false”常量的平台,渲染为“0”:
from sqlalchemy.sql import expression from sqlalchemy.ext.compiler import compiles class sql_false(expression.ColumnElement): inherit_cache = True @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) )
对象名称 | 描述 |
compiles(class_, *specs) | 为给定ClauseElement 类型注册函数作为编译器。 |
deregister(class_) | 删除与给定ClauseElement 类型关联的所有自定义编译器。 |
function sqlalchemy.ext.compiler.compiles(class_, *specs)
为给定ClauseElement
类型注册函数作为编译器。
function sqlalchemy.ext.compiler.deregister(class_)
删除与给定ClauseElement
类型关联的所有自定义编译器。
概要
使用涉及创建一个或多个ClauseElement
子类和一个或多个定义其编译的可调用对象:
from sqlalchemy.ext.compiler import compiles from sqlalchemy.sql.expression import ColumnClause class MyColumn(ColumnClause): inherit_cache = True @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): inherit_cache = False 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)
当使用任何 postgresql
方言时,将调用第二个 visit_alter_table
。
编译自定义表达式结构的子元素
compiler
参数是正在使用的 Compiled
对象。此对象可以用于检查关于正在进行的编译的任何信息,包括 compiler.dialect
、compiler.statement
等。SQLCompiler
和 DDLCompiler
都包含一个 process()
方法,可用于编译嵌入属性:
from sqlalchemy.sql.expression import Executable, ClauseElement class InsertFromSelect(Executable, ClauseElement): inherit_cache = False 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()
方法实现。
在 SQL 和 DDL 编译器之间进行交叉编译
SQL 和 DDL 构造使用不同的基础编译器 - SQLCompiler
和 DDLCompiler
进行编译。常见的需要是从 DDL 表达式中访问 SQL 表达式的编译规则。因此,DDLCompiler
包含一个访问器 sql_compiler
,如下所示,我们生成一个嵌入了 SQL 表达式的 CHECK 约束:
@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
对象或其他“文字”对象(如引用字符串或整数的对象)的 SQL 表达式应该就地渲染,而不是作为一个绑定参数引用;在发出 DDL 时,通常不支持绑定参数。
在 SQL 和 DDL 编译器之间进行交叉编译
SQL 和 DDL 构造使用不同的基础编译器 - SQLCompiler
和 DDLCompiler
进行编译。常见的需要是从 DDL 表达式中访问 SQL 表达式的编译规则。因此,DDLCompiler
包含一个访问器 sql_compiler
,如下所示,我们生成一个嵌入了 SQL 表达式的 CHECK 约束:
@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
对象或其他“文字”对象(如引用字符串或整数的对象)的 SQL 表达式应该就地渲染,而不是作为一个绑定参数引用;在发出 DDL 时,通常不支持绑定参数。
更改现有构造的默认编译
编译器扩展同样适用于现有构造。当重写内置 SQL 构造的编译时,@compiles 装饰器会在适当的类上调用(确保使用类,即 Insert
或 Select
,而不是创建函数,如 insert()
或 select()
)。
在新的编译函数中,要获取“原始”编译例程,使用适当的 visit_XXX 方法 - 这是因为编译器.process() 将调用重写例程并导致无限循环。例如,要向所有插入语句添加“前缀”:
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
也适用于类型,比如下面我们为 String
/VARCHAR
实现 MS-SQL 特定的 ‘max’ 关键字:
@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 的“比较”行为。ColumnElement
类希望有一个type
成员,该成员是表达式的返回类型。这可以在构造函数的实例级别或在类级别(如果通常是常量)中建立:
class timestamp(ColumnElement): type = TIMESTAMP() inherit_cache = True
FunctionElement
- 这是ColumnElement
和“from clause”类似对象的混合体,表示 SQL 函数或存储过程类型的调用。由于大多数数据库支持类似“SELECT FROM ”的语句,FunctionElement
添加了在select()
构造的 FROM 子句中使用的能力:
from sqlalchemy.sql.expression import FunctionElement class coalesce(FunctionElement): name = 'coalesce' inherit_cache = True @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)
ExecutableDDLElement
- 所有 DDL 表达式的根,比如 CREATE TABLE,ALTER TABLE 等。ExecutableDDLElement
的子类的编译由DDLCompiler
发出,而不是SQLCompiler
。ExecutableDDLElement
还可以与诸如DDLEvents.before_create()
和DDLEvents.after_create()
等事件钩子一起用作事件钩子,允许在 CREATE TABLE 和 DROP TABLE 序列期间自动调用构造。
另请参阅
自定义 DDL - 包含将DDL
对象(它们本身是ExecutableDDLElement
实例)与DDLEvents
事件钩子相关联的示例。Executable
- 这是一个混合类,应该与表示“独立”SQL 语句的任何表达式类一起使用,可以直接传递给execute()
方法。 它已经隐式地存在于DDLElement
和FunctionElement
中。
上述大多数构造也会响应 SQL 语句缓存。 子类化的构造将希望为对象定义缓存行为,这通常意味着将标志 inherit_cache
设置为 False
或 True
的值。 有关背景信息,请参见下一节 为自定义构造启用缓存支持。
SqlAlchemy 2.0 中文文档(三十七)(3)https://developer.aliyun.com/article/1562701