原文:
docs.sqlalchemy.org/en/20/contents.html
自定义 SQL 构造和编译扩展
原文:
docs.sqlalchemy.org/en/20/core/compiler.html
提供了用于创建自定义 ClauseElements 和编译器的 API。
概要
使用涉及创建一个或多个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))
产生:
代码语言:javascript复制SELECT [x], [y]
方言特定的编译规则
编译器也可以是特定于方言的。将为使用的方言调用适当的编译器:
代码语言:javascript复制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)
产生:
代码语言:javascript复制"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
对象或其他“literal”对象(如引用字符串或整数的对象)的 SQL 表达式应该原地呈现,而不是作为绑定参数引用;在发出 DDL 时,通常不支持绑定参数。
更改现有构造的默认编译
编译器扩展同样适用于现有的结构。当覆盖内置 SQL 结构的编译时,@compiles 装饰器会调用适当的类(确保使用类,即 Insert
或 Select
,而不是创建函数,比如 insert()
或 select()
)。
在新的编译函数中,要获取“原始”的编译例程,使用适当的 visit_XXX 方法 - 这是因为编译器.process() 将调用重写的例程并导致无限循环。比如,要向所有的插入语句添加“前缀”:
代码语言:javascript复制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 语句前加上“某个前缀”。
更改类型的编译
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 语句的“列”子句中(以及 order by 和 group by)放置的任何东西都可以从这个派生 - 该对象将自动具有 Python 的“比较”行为。
ColumnElement
类希望有一个 type
成员,该成员是表达式的返回类型。这可以在构造函数的实例级别或类级别上建立:
class timestamp(ColumnElement):
type = TIMESTAMP()
inherit_cache = True
FunctionElement
- 这是 ColumnElement
和“from 子句”类似对象的混合体,并表示 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
- 这是一个 mixin,应该与任何表示可以直接传递给execute()
方法的“独立”SQL 语句的表达式类一起使用。它已经隐含在DDLElement
和FunctionElement
中。
上述大部分构造也响应 SQL 语句缓存。子类构造将希望为对象定义缓存行为,这通常意味着将标志inherit_cache
设置为False
或True
的值。有关背景信息,请参见下一节为自定义构造启用缓存支持。
为自定义构造启用缓存支持
从版本 1.4 开始,SQLAlchemy 包括一个 SQL 编译缓存设施,它将允许等效的 SQL 构造缓存它们的字符串形式,以及用于从语句中获取结果的其他结构信息。
如在对象不会生成缓存键,性能影响中讨论的原因,该缓存系统的实现对于在缓存系统中包含自定义 SQL 构造和/或子类采取了保守的方法。这包括任何用户定义的 SQL 构造,包括此扩展的所有示例,除非它们明确声明能够这样做,否则默认情况下不会参与缓存。当在特定子类的类级别设置HasCacheKey.inherit_cache
属性为True
时,将指示该类的实例可以安全地进行缓存,使用直接父类的缓存键生成方案。例如,这适用于先前指示的“概要”示例:
class MyColumn(ColumnClause):
inherit_cache = True
@compiles(MyColumn)
def compile_mycolumn(element, compiler, **kw):
return "[%s]" % element.name
上面,MyColumn
类不包含任何影响其 SQL 编译的新状态;MyColumn
实例的缓存键将利用ColumnClause
超类的缓存键,这意味着它将考虑对象的类(MyColumn
)、对象的字符串名称和数据类型:
>>> MyColumn("some_name", String())._generate_cache_key()
CacheKey(
key=('0', <class '__main__.MyColumn'>,
'name', 'some_name',
'type', (<class 'sqlalchemy.sql.sqltypes.String'>,
('length', None), ('collation', None))
), bindparams=[])
对于那些在许多更大语句中可能被大量使用作为组件的对象,比如Column
子类和自定义 SQL 数据类型,尽可能启用缓存非常重要,否则可能会对性能产生负面影响。
一个包含影响其 SQL 编译的状态的对象示例是在编译自定义表达式构造的子元素中所示的一个示例;这是一个将Table
和Select
构造组合在一起的“INSERT FROM SELECT”构造,每个构造独立影响构造的 SQL 字符串生成。对于这个类,示例说明它根本不参与缓存:
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)
)
虽然上述InsertFromSelect
也可能生成由Table
和Select
组件组合而成的缓存键,但目前该 API 并不完全公开。然而,对于“INSERT FROM SELECT”构造,它仅用于特定操作,缓存并不像前面的示例那样关键。
对于在相对孤立并且通常是独立的对象,例如自定义 DML 构造,如 “INSERT FROM SELECT”,缓存通常不那么关键,因为对于这种构造缺乏缓存仅对该特定操作具有局部影响。
更多示例
“UTC 时间戳”函数
一个类似于 “CURRENT_TIMESTAMP” 的函数,但应用适当的转换,使时间为 UTC 时间。时间戳最好存储在关系型数据库中作为 UTC,不带时区。UTC 使您的数据库在夏令时结束时不会认为时间已经倒退,不带时区是因为时区就像字符编码 - 最好只在应用程序的端点(即在用户输入时转换为 UTC,在显示时重新应用所需的时区)应用它们。
对于 PostgreSQL 和 Microsoft SQL Server:
代码语言:javascript复制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()"
示例用法:
代码语言:javascript复制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)
示例用法:
代码语言:javascript复制Session.query(Account).
filter(
greatest(
Account.checking_balance,
Account.savings_balance) > 10000
)
“false” 表达式
渲染“false”常量表达式,对于没有“false”常量的平台,渲染为“0”:
代码语言:javascript复制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"
示例用法:
代码语言:javascript复制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))
产生:
代码语言:javascript复制SELECT [x], [y]
特定于方言的编译规则
编译器也可以是特定于方言的。将为使用的方言调用适当的编译器:
代码语言:javascript复制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)
产生:
代码语言:javascript复制"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() 将调用重写例程并导致无限循环。例如,要向所有插入语句添加“前缀”:
代码语言:javascript复制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
的值。 有关背景信息,请参见下一节 为自定义构造启用缓存支持。
为自定义构造启用缓存支持
截至版本 1.4,SQLAlchemy 包括一个 SQL 编译缓存功能,它将允许等效的 SQL 构造缓存它们的字符串形式,以及用于从语句获取结果的其他结构信息。
由于讨论的原因在对象不会生成缓存键,性能影响,这个缓存系统的实现采用了一种保守的方式来包括自定义 SQL 构造和/或子类在缓存系统中。这包括任何用户定义的 SQL 构造,包括此扩展的所有示例,默认情况下将不参与缓存,除非它们明确声明能够参与缓存。当HasCacheKey.inherit_cache
属性在特定子类的类级别上设置为True
时,将表示此类的实例可以安全地缓存,使用其直接超类的缓存键生成方案。例如,这适用于先前指示的“概要”示例:
class MyColumn(ColumnClause):
inherit_cache = True
@compiles(MyColumn)
def compile_mycolumn(element, compiler, **kw):
return "[%s]" % element.name
在上述示例中,MyColumn
类不包含任何影响其 SQL 编译的新状态;MyColumn
实例的缓存键将利用 ColumnClause
超类的缓存键,这意味着它将考虑对象的类(MyColumn
)、对象的字符串名称和数据类型:
>>> MyColumn("some_name", String())._generate_cache_key()
CacheKey(
key=('0', <class '__main__.MyColumn'>,
'name', 'some_name',
'type', (<class 'sqlalchemy.sql.sqltypes.String'>,
('length', None), ('collation', None))
), bindparams=[])
对于可能在许多较大语句中自由使用的对象,例如 Column
子类和自定义 SQL 数据类型,尽可能启用缓存是很重要的,否则可能会对性能产生负面影响。
一个包含影响其 SQL 编译的状态的对象示例是在编译自定义表达式结构的子元素中所示的对象;这是一个将 Table
与 Select
构造组合在一起的“INSERT FROM SELECT”构造,它们各自独立地影响构造的 SQL 字符串生成。对于这个类,示例说明了它根本不参与缓存:
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)
)
虽然上述的 InsertFromSelect
也可能生成由 Table
和 Select
组件组成的缓存键,但目前该 API 并不完全公开。但是,对于“INSERT FROM SELECT”构造,它只用于特定操作,缓存并不像前面的示例那样关键。
对于在相对孤立并且通常是独立的对象,比如自定义 DML 构造,比如“INSERT FROM SELECT”,缓存通常不太关键,因为对于这种构造物的缺乏缓存只会对该特定操作产生局部影响。
更多示例
“UTC 时间戳”函数
一个类似于“CURRENT_TIMESTAMP”的函数,但应用适当的转换,使时间处于 UTC 时间。时间戳最好存储在关系数据库中作为 UTC 时间,不带时区。UTC 时间是为了在夏令时结束时,数据库不会认为时间倒退一小时,不带时区是因为时区就像字符编码一样——最好只在应用程序的端点应用(即在用户输入时转换为 UTC 时间,在显示时重新应用所需的时区)。
对于 PostgreSQL 和 Microsoft SQL Server:
代码语言:javascript复制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()"
示例用法:
代码语言:javascript复制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
函数。一个 SQL 标准版本与一个基于 CASE 的版本相对应,后者仅容纳两个参数:
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)
示例用法:
代码语言:javascript复制Session.query(Account).
filter(
greatest(
Account.checking_balance,
Account.savings_balance) > 10000
)
“false”表达式
渲染“false”常量表达式,在没有“false”常量的平台上呈现为“0”:
代码语言:javascript复制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"
示例用法:
代码语言:javascript复制from sqlalchemy import select, union_all
exp = union_all(
select(users.c.name, sql_false().label("enrolled")),
select(customers.c.name, customers.c.enrolled)
)
“UTC 时间戳”函数
一个类似于“CURRENT_TIMESTAMP”的函数,但应用适当的转换,使时间处于 UTC 时间。时间戳最好存储在关系数据库中作为 UTC 时间,不带时区。UTC 时间是为了在夏令时结束时,数据库不会认为时间倒退一小时,不带时区是因为时区就像字符编码一样——最好只在应用程序的端点应用(即在用户输入时转换为 UTC 时间,在显示时重新应用所需的时区)。
对于 PostgreSQL 和 Microsoft SQL Server:
代码语言:javascript复制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()"
示例用法:
代码语言:javascript复制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
函数。一个 SQL 标准版本与一个基于 CASE 的版本相对应,后者仅容纳两个参数:
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)
示例用法:
代码语言:javascript复制Session.query(Account).
filter(
greatest(
Account.checking_balance,
Account.savings_balance) > 10000
)
“false”表达式
渲染“false”常量表达式,在没有“false”常量的平台上呈现为“0”:
代码语言:javascript复制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"
示例用法:
代码语言:javascript复制from sqlalchemy import select, union_all
exp = union_all(
select(users.c.name, sql_false().label("enrolled")),
select(customers.c.name, customers.c.enrolled)
)
表达式序列化器扩展
原文:
docs.sqlalchemy.org/en/20/core/serializer.html
用于与 SQLAlchemy 查询结构一起使用的序列化器/反序列化器对象,允许“上下文”反序列化。
遗留功能
序列化器扩展是遗留的,不应用于新开发。
可以使用任何 SQLAlchemy 查询结构,无论是基于 sqlalchemy.sql.* 还是 sqlalchemy.orm.*。结构引用的映射器、表、列、会话等在序列化形式中不会被持久化,而是在反序列化时重新关联到查询结构。
警告
序列化器扩展使用 pickle 对对象进行序列化和反序列化,因此与 python 文档 中提到的相同的安全注意事项适用。
使用方式几乎与标准 Python pickle 模块相同:
代码语言:javascript复制from sqlalchemy.ext.serializer import loads, dumps
metadata = MetaData(bind=some_engine)
Session = scoped_session(sessionmaker())
# ... define mappers
query = Session.query(MyClass).
filter(MyClass.somedata=='foo').order_by(MyClass.sortkey)
# pickle the query
serialized = dumps(query)
# unpickle. Pass in metadata scoped_session
query2 = loads(serialized, metadata, Session)
print query2.all()
使用原始 pickle 时适用的类似限制也适用;映射类必须本身可被 pickle 化,这意味着它们可以从模块级别的命名空间导入。
序列化器模块仅适用于查询结构。不需要:
- 用户定义类的实例。在典型情况下,这些类不包含对引擎、会话或表达式构造的引用,因此可以直接序列化。
- 完全从序列化结构加载的表元数据(即在应用程序中尚未声明的元数据)。可以使用常规的 pickle.loads()/dumps() 来完全转储任何
MetaData
对象,通常是在以前的某个时间点从现有数据库反射的对象。序列化器模块专门用于相反的情况,即表元数据已经存在于内存中的情况。
对象名称 | 描述 |
---|---|
Deserializer(file[, metadata, scoped_session, engine]) | |
dumps(obj[, protocol]) | |
loads(data[, metadata, scoped_session, engine]) | |
Serializer(*args, **kw) |
function sqlalchemy.ext.serializer.Deserializer(file, metadata=None, scoped_session=None, engine=None)
代码语言:javascript复制function sqlalchemy.ext.serializer.Serializer(*args, **kw)
代码语言:javascript复制function sqlalchemy.ext.serializer.dumps(obj, protocol=5)
代码语言:javascript复制function sqlalchemy.ext.serializer.loads(data, metadata=None, scoped_session=None, engine=None)
SQL 表达语言基础构造
原文:
docs.sqlalchemy.org/en/20/core/foundation.html
用于组成 SQL 表达语言元素的基类和混合类。
对象名称 | 描述 |
---|---|
CacheKey | 用于在 SQL 编译缓存中标识 SQL 语句构造的键。 |
ClauseElement | 用于程序化构建 SQL 表达式的元素的基类。 |
DialectKWArgs | 建立一个类具有特定方言参数的能力,带有默认值和构造函数验证。 |
HasCacheKey | 用于能够生成缓存键的对象的混合类。 |
LambdaElement | 一个 SQL 构造,其中状态存储为未调用的 lambda。 |
StatementLambdaElement | 代表一个可组合的 SQL 语句,作为LambdaElement。 |
class sqlalchemy.sql.expression.CacheKey
用于在 SQL 编译缓存中标识 SQL 语句构造的键。
另请参阅
SQL 编译缓存
成员
bindparams, key, to_offline_string()
类签名
类sqlalchemy.sql.expression.CacheKey
(builtins.tuple
)
attribute bindparams: Sequence[BindParameter[Any]]
字段编号 1 的别名
代码语言:javascript复制attribute key: Tuple[Any, ...]
字段编号 0 的别名
代码语言:javascript复制method to_offline_string(statement_cache: MutableMapping[Any, str], statement: ClauseElement, parameters: _CoreSingleExecuteParams) → str
生成这个CacheKey
的“离线字符串”形式
“离线字符串”基本上是语句的字符串 SQL 加上一系列绑定参数值的 repr。而CacheKey
对象依赖于内存中的标识以便作为缓存键工作,“离线”版本适用于其他进程也能工作的缓存。
给定的statement_cache
是一个类似字典的对象,其中语句本身的字符串形式将被缓存。为了减少字符串化语句所花费的时间,这个字典应该在一个更长寿命的范围内。
class sqlalchemy.sql.expression.ClauseElement
用于程序化构建 SQL 表达式的元素的基类。
成员
compare(), compile(), get_children(), inherit_cache, params(), self_group(), unique_params()
类签名
类sqlalchemy.sql.expression.ClauseElement
(sqlalchemy.sql.annotation.SupportsWrappingAnnotations
、sqlalchemy.sql.cache_key.MemoizedHasCacheKey
、sqlalchemy.sql.traversals.HasCopyInternals
、sqlalchemy.sql.visitors.ExternallyTraversible
、sqlalchemy.sql.expression.CompilerElement
)
method compare(other: ClauseElement, **kw: Any) → bool
将此ClauseElement
与给定的ClauseElement
进行比较。
子类应该覆盖默认行为,即直接进行身份比较。
**kw 是子类compare()
方法消耗的参数,可用于修改比较的标准(参见ColumnElement
)。
method compile(bind: _HasDialect | None = None, dialect: Dialect | None = None, **kw: Any) → Compiled
从 CompilerElement
的 CompilerElement.compile()
方法继承
编译此 SQL 表达式。
返回值是一个Compiled
对象。对返回值调用str()
或unicode()
将产生结果的字符串表示。Compiled
对象还可以使用params
访问器返回绑定参数名称和值的字典。
参数:
bind
– 一个Connection
或Engine
,它可以提供一个Dialect
以生成一个Compiled
对象。如果bind
和dialect
参数都被省略,将使用默认的 SQL 编译器。
column_keys
– 用于 INSERT 和 UPDATE 语句,一个应该存在于编译后语句的 VALUES 子句中的列名列表。如果为None
,则从目标表对象中渲染所有列。
dialect
– 一个Dialect
实例,可以生成一个Compiled
对象。此参数优先于bind
参数。
compile_kwargs
–
额外参数的可选字典,这些参数将通过所有“访问”方法传递给编译器。这允许通过到自定义编译结构的任何自定义标志进行传递。它还用于通过以下方式传递 literal_binds
标志的情况:
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}))
另请参阅
我如何将 SQL 表达式呈现为字符串,可能还包含内联的绑定参数?
代码语言:javascript复制method get_children(*, omit_attrs: Tuple[str, ...] = (), **kw: Any) → Iterable[HasTraverseInternals]
从 HasTraverseInternals.get_children()
方法继承 HasTraverseInternals
返回此 HasTraverseInternals
的直接子元素 HasTraverseInternals
。
这用于访问遍历。
**kw 可包含更改返回集合的标志,例如返回子集以减少较大的遍历,或从不同上下文(例如模式级集合而不是子句级)返回子项的标志。
代码语言:javascript复制attribute inherit_cache: bool | None = None
从 HasCacheKey
的 HasCacheKey.inherit_cache
属性继承
指示此 HasCacheKey
实例是否应使用其直接超类使用的缓存键生成方案。
属性默认为 None
,表示构造尚未考虑其是否适合参与缓存;这在功能上等同于将值设置为 False
,只是还会发出警告。
如果对应于对象的 SQL 不根据此类的本地属性(而不是其超类)更改,则可以在特定类上将此标志设置为 True
。
另请参阅
为自定义构造启用缓存支持 - 设置第三方或用户定义的 SQL 构造的 HasCacheKey.inherit_cache
属性的一般指南。
method params(_ClauseElement__optionaldict: Mapping[str, Any] | None = None, **kwargs: Any) → Self
返回一个副本,其中 bindparam()
元素已被替换。
返回此 ClauseElement 的副本,并用从给定字典中取出的值替换其中的 bindparam()
元素:
>>> clause = column('x') bindparam('foo')
>>> print(clause.compile().params)
{'foo':None}
>>> print(clause.params({'foo':7}).compile().params)
{'foo':7}
代码语言:javascript复制method self_group(against: OperatorType | None = None) → ClauseElement
对此 ClauseElement
应用‘分组’。
子类重写此方法以返回一个“分组”构造,即括号。特别是当“二进制”表达式被放置到更大的表达式中时,它们会提供一个围绕自身的分组,以及当 select()
构造被放置到另一个 select()
的 FROM 子句中时。 (请注意,子查询通常应该使用 Select.alias()
方法创建,因为许多平台要求嵌套的 SELECT 语句必须被命名)。
随着表达式的组合,self_group()
的应用是自动的 - 最终用户代码不应直接使用此方法。请注意,SQLAlchemy 的子句构造考虑了运算符优先级 - 因此在像 x OR (y AND z)
这样的表达式中可能不需要括号 - AND 优先于 OR。
ClauseElement
的基础 self_group()
方法只返回自身。
method unique_params(_ClauseElement__optionaldict: Dict[str, Any] | None = None, **kwargs: Any) → Self
返回一个将 bindparam()
元素替换的副本。
与 ClauseElement.params()
相同的功能,只是对影响到的绑定参数添加了 unique=True,以便可以使用多个语句。
class sqlalchemy.sql.base.DialectKWArgs
建立类具有方言特定参数的能力,并具有默认值和构造函数验证。
DialectKWArgs
与方言上的 DefaultDialect.construct_arguments
交互。
成员
argument_for(), dialect_kwargs, dialect_options, kwargs
另请参阅
DefaultDialect.construct_arguments
classmethod argument_for(dialect_name, argument_name, default)
为这个类添加一种新的方言特定的关键字参数。
例如:
代码语言:javascript复制Index.argument_for("mydialect", "length", None)
some_index = Index('a', 'b', mydialect_length=5)
DialectKWArgs.argument_for()
方法是一种逐个参数地向DefaultDialect.construct_arguments
字典添加额外参数的方式。该字典提供了各种模式级构造的方言接受的参数名称列表。
新方言通常应一次性指定该字典作为方言类的数据成员。通常情况下,用于临时添加参数名称的用例是为了终端用户代码,该代码还使用了消耗额外参数的自定义编译方案。
参数:
-
dialect_name
– 方言的名称。方言必须是可定位的,否则会引发NoSuchModuleError
。方言还必须包含一个现有的DefaultDialect.construct_arguments
集合,指示其参与关键字参数验证和默认系统,否则会引发ArgumentError
。如果方言不包含此集合,则已经可以为该方言指定任何关键字参数。SQLAlchemy 内置的所有方言都包含此集合,但对于第三方方言,支持可能有所不同。 -
argument_name
– 参数的名称。 -
default
– 参数的默认值。
attribute dialect_kwargs
作为特定方言选项指定的关键字参数集合。
这里的参数以其原始的<dialect>_<kwarg>
格式呈现。只包括实际传递的参数;不同于DialectKWArgs.dialect_options
集合,后者包含了该方言已知的所有选项,包括默认值。
该集合也是可写的;接受形式为<dialect>_<kwarg>
的键,其值将被组装到选项列表中。
另请参阅
DialectKWArgs.dialect_options
- 嵌套字典形式
attribute dialect_options
作为特定方言选项指定的关键字参数集合。
这是一个两级嵌套的注册表,以<dialect_name>
和<argument_name>
为键。例如,postgresql_where
参数可以定位为:
arg = my_object.dialect_options['postgresql']['where']
0.9.2 版本中新增。
另请参阅
DialectKWArgs.dialect_kwargs
- 扁平字典形式
attribute kwargs
DialectKWArgs.dialect_kwargs
的别名。
class sqlalchemy.sql.traversals.HasCacheKey
用于可以生成缓存键的对象的混合类。
此类通常位于以 HasTraverseInternals
为基础的层次结构中,但这是可选的。目前,该类应该能够在不包括 HasTraverseInternals
的情况下独立工作。
成员
inherit_cache
另请参阅
CacheKey
SQL 编译缓存
代码语言:javascript复制attribute inherit_cache: bool | None = None
表明此 HasCacheKey
实例是否应该使用其直接超类使用的缓存键生成方案。
该属性默认为 None
,表示构造尚未考虑是否适合参与缓存;这在功能上等同于将值设置为 False
,除了还会发出警告。
如果对象对应的 SQL 不基于仅限于此类而非其超类的属性发生变化,则可以在特定类上将此标志设置为 True
。
另请参阅
为自定义构造启用缓存支持 - 设置第三方或用户定义的 SQL 构造的 HasCacheKey.inherit_cache
属性的通用指南。
class sqlalchemy.sql.expression.LambdaElement
一个 SQL 构造,其中状态被存储为未调用的 lambda。
LambdaElement
在将 lambda 表达式传递给 SQL 构造时会透明地生成,例如:
stmt = select(table).where(lambda: table.c.col == parameter)
LambdaElement
是 StatementLambdaElement
的基础,它代表了 lambda 中的完整语句。
新版本 1.4 中新增。
另请参阅
使用 Lambdas 为语句生成带来显著的速度提升
类签名
类 sqlalchemy.sql.expression.LambdaElement
(sqlalchemy.sql.expression.ClauseElement
)
class sqlalchemy.sql.expression.StatementLambdaElement
将可组合的 SQL 语句表示为 LambdaElement
。
使用 lambda_stmt()
函数构建 StatementLambdaElement
:
from sqlalchemy import lambda_stmt
stmt = lambda_stmt(lambda: select(table))
构建完成后,可以通过添加后续 lambda 将额外条件添加到语句中,这些 lambda 将现有语句对象作为单个参数接受:
代码语言:javascript复制stmt = lambda s: s.where(table.c.col == parameter)
版本 1.4 中的新功能。
另请参阅
使用 Lambda 添加显著的语句生成速度提升
成员
add_criteria(), is_delete, is_dml, is_insert, is_select, is_text, is_update, spoil()
类签名
类 sqlalchemy.sql.expression.StatementLambdaElement
(sqlalchemy.sql.roles.AllowsLambdaRole
, sqlalchemy.sql.expression.LambdaElement
, sqlalchemy.sql.expression.Executable
)
method add_criteria(other: Callable[[Any], Any], enable_tracking: bool = True, track_on: Any | None = None, track_closure_variables: bool = True, track_bound_values: bool = True) → StatementLambdaElement
向此 StatementLambdaElement
添加新条件。
例如:
代码语言:javascript复制>>> def my_stmt(parameter):
... stmt = lambda_stmt(
... lambda: select(table.c.x, table.c.y),
... )
... stmt = stmt.add_criteria(
... lambda: table.c.x > parameter
... )
... return stmt
StatementLambdaElement.add_criteria()
方法等同于使用 Python 加法运算符添加新的 lambda,不过可以添加额外的参数,包括 track_closure_values
和 track_on
:
>>> def my_stmt(self, foo):
... stmt = lambda_stmt(
... lambda: select(func.max(foo.x, foo.y)),
... track_closure_variables=False
... )
... stmt = stmt.add_criteria(
... lambda: self.where_criteria,
... track_on=[self]
... )
... return stmt
有关可接受参数的说明,请参阅 lambda_stmt()
。
attribute is_delete
代码语言:javascript复制attribute is_dml
代码语言:javascript复制attribute is_insert
代码语言:javascript复制attribute is_select
代码语言:javascript复制attribute is_text
代码语言:javascript复制attribute is_update
代码语言:javascript复制method spoil() → NullLambdaStatement
返回一个新的 StatementLambdaElement
,每次运行所有 lambda 时都会无条件地运行。
访问者和遍历实用程序
原文:
docs.sqlalchemy.org/en/20/core/visitors.html
sqlalchemy.sql.visitors
模块由用于通用地 遍历 核心 SQL 表达式结构的类和函数组成。这与 Python 的 ast
模块类似,因为它提供了一个程序可以操作 SQL 表达式每个组件的系统。它通常用于定位各种类型的元素,如 Table
或 BindParameter
对象,以及更改结构状态,如使用其他 FROM 子句替换某些 FROM 子句。
注意
sqlalchemy.sql.visitors
模块是一个内部 API,不是完全公开的。它可能会发生变化,而且对于不考虑 SQLAlchemy 内部工作方式的使用模式可能无法正常运行。
sqlalchemy.sql.visitors
模块是 SQLAlchemy 的 内部 部分,通常不会由调用应用程序代码使用。但是,在某些边缘情况下会使用它,例如构建缓存例程以及使用 自定义 SQL 构造和编译扩展 构建自定义 SQL 表达式时。
访问者/遍历接口和库函数。
对象名称 | 描述 |
---|---|
anon_map | cache_anon_map 的别名 |
cloned_traverse(obj, opts, visitors) | 克隆给定的表达式结构,允许访问者修改可变对象。 |
ExternalTraversal | 用于可以使用 traverse() 函数进行外部遍历的访问者对象的基类。 |
InternalTraversal | 定义用于内部遍历的访问者符号。 |
iterate(obj[, opts]) | 遍历给定的表达式结构,返回一个迭代器。 |
replacement_traverse(obj, opts, replace) | 克隆给定的表达式结构,允许使用给定的替换函数进行元素替换。 |
traverse(obj, opts, visitors) | 使用默认迭代器遍历给定的表达式结构并访问。 |
traverse_using(iterator, obj, visitors) | 使用给定的对象迭代器访问给定的表达式结构。 |
Visitable | 可访问对象的基类。 |
class sqlalchemy.sql.visitors.ExternalTraversal
用于使用traverse()
函数进行外部遍历的访问者对象的基类。
直接使用traverse()
函数通常更可取。
成员
chain(), iterate(), traverse(), visitor_iterator
类签名
类sqlalchemy.sql.visitors.ExternalTraversal
(sqlalchemy.util.langhelpers.MemoizedSlots
)
method chain(visitor: ExternalTraversal) → _ExtT
在此 ExternalTraversal 上“链接”一个额外的 ExternalTraversal
连接的访问者将在此后接收所有访问事件。
代码语言:javascript复制method iterate(obj: ExternallyTraversible | None) → Iterator[ExternallyTraversible]
遍历给定的表达式结构,返回所有元素的迭代器。
代码语言:javascript复制method traverse(obj: ExternallyTraversible | None) → ExternallyTraversible | None
遍历并访问给定的表达式结构。
代码语言:javascript复制attribute visitor_iterator
通过此访问者和每个“链接”访问者进行迭代。
代码语言:javascript复制class sqlalchemy.sql.visitors.InternalTraversal
定义用于内部遍历的访问者符号。
InternalTraversal
类有两种用法。一种是它可以作为一个实现该类各种访问方法的对象的超类。另一种是InternalTraversal
自身的符号被用在_traverse_internals
集合中。例如,Case
对象将_traverse_internals
定义为
class Case(ColumnElement[_T]):
_traverse_internals = [
("value", InternalTraversal.dp_clauseelement),
("whens", InternalTraversal.dp_clauseelement_tuples),
("else_", InternalTraversal.dp_clauseelement),
]
在上面,Case
类将其内部状态表示为名为value
、whens
和else_
的属性。它们各自链接到一个InternalTraversal
方法,该方法指示每个属性引用的数据结构类型。
使用_traverse_internals
结构,InternalTraversible
类型的对象将自动实现以下方法:
-
HasTraverseInternals.get_children()
-
HasTraverseInternals._copy_internals()
-
HasCacheKey._gen_cache_key()
子类还可以直接实现这些方法,特别是HasTraverseInternals._copy_internals()
方法,当需要特殊步骤时。
版本 1.4 中的新功能。
成员
dp_annotations_key, dp_anon_name, dp_boolean, dp_clauseelement, dp_clauseelement_list, dp_clauseelement_tuple, dp_clauseelement_tuples, dp_dialect_options, dp_dml_multi_values, dp_dml_ordered_values, dp_dml_values, dp_fromclause_canonical_column_collection, dp_fromclause_ordered_set, dp_has_cache_key, dp_has_cache_key_list, dp_has_cache_key_tuples, dp_ignore, dp_inspectable, dp_inspectable_list, dp_multi, dp_multi_list, dp_named_ddl_element, dp_operator, dp_plain_dict, dp_plain_obj, dp_prefix_sequence, dp_propagate_attrs, dp_statement_hint_list, dp_string, dp_string_clauseelement_dict, dp_string_list, dp_string_multi_dict, dp_table_hint_list, dp_type, dp_unknown_structure
类签名
类 sqlalchemy.sql.visitors.InternalTraversal
(enum.Enum
)。
attribute dp_annotations_key = 'AK'
访问 _annotations_cache_key 元素。
这是有关修改其角色的 ClauseElement 的其他信息的字典。在比较或缓存对象时应包括此信息,但是生成此键相对昂贵。在创建此键之前,访问者应首先检查“_annotations”字典是否为非 None。
代码语言:javascript复制attribute dp_anon_name = 'AN'
访问可能“匿名化”的字符串值。
字符串值被视为缓存键生成的重要因素。
代码语言:javascript复制attribute dp_boolean = 'B'
访问布尔值。
布尔值被视为缓存键生成的重要因素。
代码语言:javascript复制attribute dp_clauseelement = 'CE'
访问 ClauseElement
对象。
attribute dp_clauseelement_list = 'CL'
访问包含 ClauseElement
对象的列表。
attribute dp_clauseelement_tuple = 'CT'
访问包含 ClauseElement
对象的元组。
attribute dp_clauseelement_tuples = 'CTS'
访问包含 ClauseElement
对象的元组列表。
attribute dp_dialect_options = 'DO'
访问方言选项结构。
代码语言:javascript复制attribute dp_dml_multi_values = 'DML_MV'
访问 Insert
对象的字典的值(值为多个)。
attribute dp_dml_ordered_values = 'DML_OV'
访问 Update
对象的有序元组列表的值。
attribute dp_dml_values = 'DML_V'
访问 ValuesBase
(例如 Insert 或 Update)对象的字典的值。
attribute dp_fromclause_canonical_column_collection = 'FC'
访问 FromClause
对象的 columns
属性的上下文中。
列集合是“规范的”,这意味着它是 ColumnClause
对象的最初定义位置。目前这意味着正在访问的对象只能是 TableClause
或 Table
对象。
attribute dp_fromclause_ordered_set = 'CO'
访问 FromClause
对象的有序集合。
attribute dp_has_cache_key = 'HC'
访问 HasCacheKey
对象。
attribute dp_has_cache_key_list = 'HL'
访问包含 HasCacheKey
对象的列表。
attribute dp_has_cache_key_tuples = 'HT'
访问包含 HasCacheKey
对象的元组列表。
attribute dp_ignore = 'IG'
指定应完全忽略的对象。
这目前适用于函数调用参数缓存,其中一些参数不应被视为缓存键的一部分。
代码语言:javascript复制attribute dp_inspectable = 'IS'
访问可检查对象,其返回值是HasCacheKey
对象。
attribute dp_inspectable_list = 'IL'
访问可检查对象的列表,在检查后是HasCacheKey
对象。
attribute dp_multi = 'M'
访问可能是HasCacheKey
或可能是普通可哈希对象的对象。
attribute dp_multi_list = 'MT'
访问包含可能是HasCacheKey
或可能是普通可哈希对象的元组。
attribute dp_named_ddl_element = 'DD'
访问简单的命名 DDL 元素。
此方法使用的当前对象是Sequence
。
该对象仅在缓存键生成中被认为是重要的,就其名称而言,但不涉及其它方面。
代码语言:javascript复制attribute dp_operator = 'O'
访问一个运算符。
运算符是sqlalchemy.sql.operators
模块中的函数。
运算符值被认为在缓存键生成中是重要的。
代码语言:javascript复制attribute dp_plain_dict = 'PD'
访问具有字符串键的字典。
字典的键应该是字符串,值应该是不可变的和可哈希的。 字典被认为在缓存键生成中是重要的。
代码语言:javascript复制attribute dp_plain_obj = 'PO'
访问普通的 Python 对象。
值应该是不可变的和可哈希的,例如整数。 值被认为在缓存键生成中是重要的。
代码语言:javascript复制attribute dp_prefix_sequence = 'PS'
访问由HasPrefixes
或HasSuffixes
表示的序列。
attribute dp_propagate_attrs = 'PA'
访问传播属性字典。 这是硬编码到我们目前关心的特定元素。
代码语言:javascript复制attribute dp_statement_hint_list = 'SH'
访问Select
对象的_statement_hints
集合。
attribute dp_string = 'S'
访问普通的字符串值。
例如,表名和列名,绑定参数键,特殊关键字如“UNION”,“UNION ALL”。
字符串值被认为在缓存键生成中是重要的。
代码语言:javascript复制attribute dp_string_clauseelement_dict = 'CD'
访问具有字符串键到ClauseElement
对象的字典。
attribute dp_string_list = 'SL'
访问字符串列表。
代码语言:javascript复制attribute dp_string_multi_dict = 'MD'
访问具有字符串键和值的字典,值可能是普通的不可变/可哈希的对象,也可能是HasCacheKey
对象。
attribute dp_table_hint_list = 'TH'
访问Select
对象的_hints
集合。
attribute dp_type = 'T'
访问TypeEngine
对象。
类型对象被认为对缓存键生成很重要。
代码语言:javascript复制attribute dp_unknown_structure = 'UK'
访问一个未知的结构。
代码语言:javascript复制class sqlalchemy.sql.visitors.Visitable
用于可访问对象的基类。
Visitable
用于实现 SQL 编译器分发函数。其他形式的遍历,例如用于缓存键生成的遍历,是使用 HasTraverseInternals
接口单独实现的。
在版本 2.0 中发生了变化:1.4 系列中的 Visitable
类被命名为 Traversible
;该名称在 2.0 中改回了 Visitable
,这是 1.4 之前的名称。
在 1.4 和 2.0 版本中,这两个名称仍然可导入。
代码语言:javascript复制attribute sqlalchemy.sql.visitors.anon_map
cache_anon_map
的别名
function sqlalchemy.sql.visitors.cloned_traverse(obj: ExternallyTraversible | None, opts: Mapping[str, Any], visitors: Mapping[str, Callable[[Any], None]]) → ExternallyTraversible | None
克隆给定的表达式结构,允许访问者修改可变对象。
遍历用法与 traverse()
相同。visitors
字典中的访问者函数也可以在遍历过程中修改给定结构的内部。
cloned_traverse()
函数不会提供属于Immutable
接口的对象给访问方法(这主要包括 ColumnClause
、Column
、TableClause
和 Table
对象)。由于此遍历仅旨在允许对象的原地突变,因此跳过Immutable
对象。仍然在每个对象上调用 Immutable._clone()
方法,以允许对象根据其子内部的克隆替换自身为不同的对象(例如,一个克隆其子查询以返回一个新的 ColumnClause
的 ColumnClause
)。
在版本 2.0 中发生了变化:cloned_traverse()
函数省略了属于Immutable
接口的对象。
除了用于实现迭代的 ClauseElement.get_children()
函数外,cloned_traverse()
和 replacement_traverse()
函数使用的中心 API 特性是 ClauseElement._copy_internals()
方法。要正确支持克隆和替换遍历的 ClauseElement
结构,它需要能够将克隆函数传递给其内部成员,以便对其进行复制。
另请参阅
traverse()
replacement_traverse()
function sqlalchemy.sql.visitors.iterate(obj: ExternallyTraversible | None, opts: Mapping[str, Any] = {}) → Iterator[ExternallyTraversible]
遍历给定的表达式结构,返回一个迭代器。
遍历配置为广度优先。
iterate()
函数使用的中心 API 特性是 ClauseElement.get_children()
方法,用于 ClauseElement
对象。该方法应返回与特定 ClauseElement
对象关联的所有 ClauseElement
对象。例如,Case
结构将在其 “whens” 和 “else_” 成员变量中引用一系列 ColumnElement
对象。
参数:
-
obj
– 要遍历的ClauseElement
结构 -
opts
– 迭代选项的字典。在现代用法中,此字典通常为空。
function sqlalchemy.sql.visitors.replacement_traverse(obj: ExternallyTraversible | None, opts: Mapping[str, Any], replace: _TraverseTransformCallableType[Any]) → ExternallyTraversible | None
克隆给定的表达式结构,允许通过给定的替换函数进行元素替换。
此函数与cloned_traverse()
函数非常相似,不同之处在于,该函数不是被传递一个访问者字典,而是所有元素都无条件地传递给给定的替换函数。然后,替换函数可以选择返回一个完全新的对象,该对象将替换给定的对象。如果返回None
,则保留对象在原位。
cloned_traverse()
和replacement_traverse()
之间的使用差异在于,在前一种情况下,已克隆的对象被传递给访问者函数,然后访问者函数可以操作对象的内部状态。在后一种情况下,访问者函数应该只返回一个完全不同的对象,或者什么也不做。
replacement_traverse()
的用例是在 SQL 结构内部用不同的 FROM 子句替换一个 FROM 子句,这是 ORM 中常见的用例。
function sqlalchemy.sql.visitors.traverse(obj: ExternallyTraversible | None, opts: Mapping[str, Any], visitors: Mapping[str, Callable[[Any], None]]) → ExternallyTraversible | None
使用默认迭代器遍历和访问给定的表达式结构。
例如:
代码语言:javascript复制from sqlalchemy.sql import visitors
stmt = select(some_table).where(some_table.c.foo == 'bar')
def visit_bindparam(bind_param):
print("found bound value: %s" % bind_param.value)
visitors.traverse(stmt, {}, {"bindparam": visit_bindparam})
对象的迭代使用iterate()
函数,该函数使用堆栈进行广度优先遍历。
参数:
-
obj
– 要遍历的ClauseElement
结构 -
opts
– 迭代选项的字典。在现代用法中,该字典通常为空。 -
visitors
– 访问函数的字典。该字典应该有字符串作为键,每个键对应于特定类型的 SQL 表达式对象的__visit_name__
,并且可调用的函数作为值,每个值代表该类型对象的访问函数。
function sqlalchemy.sql.visitors.traverse_using(iterator: Iterable[ExternallyTraversible], obj: ExternallyTraversible | None, visitors: Mapping[str, Callable[[Any], None]]) → ExternallyTraversible | None
使用给定的对象迭代器访问给定的表达式结构。
traverse_using()
通常在内部作为traverse()
函数的结果而调用。
参数:
-
iterator
– 一个可迭代或序列,它将生成ClauseElement
结构;假定该迭代器是iterate()
函数的产品。 -
obj
– 作为iterate()
函数目标使用的ClauseElement
。 -
visitors
– 访问函数的字典。有关此字典的详细信息,请参见traverse()
。
另请参阅
traverse()
模式定义语言
原文:
docs.sqlalchemy.org/en/20/core/schema.html
本节涉及 SQLAlchemy 模式元数据,这是一种全面描述和检查数据库模式的系统。
SQLAlchemy 查询和对象映射操作的核心由 数据库元数据 支持,它由描述表和其他模式级对象的 Python 对象组成。这些对象是三种主要类型操作的核心 - 发出 CREATE 和 DROP 语句(称为 DDL)、构造 SQL 查询以及表达有关已存在于数据库中的结构的信息。
数据库元数据可以通过显式命名各种组件及其属性来表示,使用诸如 Table
、Column
、ForeignKey
和 Sequence
等构造,所有这些都从 sqlalchemy.schema
包中导入。它也可以由 SQLAlchemy 使用称为 反射 的过程生成,这意味着您从一个单一对象(例如 Table
)开始,为其指定一个名称,然后指示 SQLAlchemy 从特定的引擎源加载与该名称相关的所有附加信息。
SQLAlchemy 数据库元数据构造的一个关键特性是它们设计成以 声明式 风格使用,这与真实的 DDL 非常相似。因此,对于那些有一定创建真实模式生成脚本背景的人来说,它们是最直观的。
- 使用 MetaData 描述数据库
- 访问表和列
- 创建和删除数据库表
- 通过迁移修改数据库对象
- 指定模式名称
- 使用 MetaData 指定默认模式名称
- 应用动态模式命名约定
- 为新连接设置默认模式
- 模式和反射
- 特定于后端的选项
- 列、表、MetaData API
-
Column
-
MetaData
-
SchemaConst
-
SchemaItem
-
insert_sentinel()
-
Table
-
- 反射数据库对象
- 覆盖反射列
- 反射视图
- 一次性反射所有表
- 从其他模式反射表
- 模式限定的反射与默认模式的交互
- 使用检查器进行精细反射
-
Inspector
-
ReflectedColumn
-
ReflectedComputed
-
ReflectedCheckConstraint
-
ReflectedForeignKeyConstraint
-
ReflectedIdentity
-
ReflectedIndex
-
ReflectedPrimaryKeyConstraint
-
ReflectedUniqueConstraint
-
ReflectedTableComment
-
- 使用数据库无关类型进行反射
- 反射的限制
- 列的插入/更新默认值
- 标量默认值
- Python 执行函数
- 上下文敏感的默认函数
- 客户端调用的 SQL 表达式
- 服务器调用的 DDL 显式默认表达式
- 标记隐式生成的值、时间戳和触发列
- 定义序列
- 将序列关联到 SERIAL 列
- 独立执行序列
- 将序列与 MetaData 关联
- 将序列关联为服务器端默认值
- 计算列(GENERATED ALWAYS AS)
- 标识列(GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY)
- 默认对象 API
-
Computed
-
ColumnDefault
-
DefaultClause
-
DefaultGenerator
-
FetchedValue
-
Sequence
-
Identity
-
- 定义约束和索引
- 定义外键
- 通过 ALTER 创建/删除外键约束
- ON UPDATE 和 ON DELETE
- 唯一约束
- CHECK 约束
- 主键约束
- 在使用声明性 ORM 扩展时设置约束
- 配置约束命名约定
- 为 MetaData 集合配置���名约定
- 默认命名约定
- 截断长名称
- 为命名约定创建自定义标记
- 命名 CHECK 约束
- 为布尔值、枚举和其他模式类型配置命名
- 在 ORM 声明性混合中使用命名约定
- 约束 API
-
Constraint
-
ColumnCollectionMixin
-
ColumnCollectionConstraint
-
CheckConstraint
-
ForeignKey
-
ForeignKeyConstraint
-
HasConditionalDDL
-
PrimaryKeyConstraint
-
UniqueConstraint
-
conv()
-
- 索引
- 函数索引
- 索引 API
Index
- 定义外键
- 自定义 DDL
- 自定义 DDL
- 控制 DDL 序列
- 使用内置的 DDLElement 类
- 控制约束和索引的 DDL 生成
- DDL 表达式构造 API
-
sort_tables()
-
sort_tables_and_constraints()
-
BaseDDLElement
-
ExecutableDDLElement
-
DDL
-
_CreateDropBase
-
CreateTable
-
DropTable
-
CreateColumn
-
CreateSequence
-
DropSequence
-
CreateIndex
-
DropIndex
-
AddConstraint
-
DropConstraint
-
CreateSchema
-
DropSchema
-