SqlAlchemy 2.0 中文文档(七十八)(3)https://developer.aliyun.com/article/1560963
新的核心功能
完全可扩展,类型级别的核心操作符支持
到目前为止,核心从未有过任何系统来为列和其他表达式构造添加对新 SQL 运算符的支持,除了ColumnOperators.op()方法,这个方法“刚好”能让事情正常运行。此外,核心从未有过任何系统允许覆盖现有运算符的行为。直到现在,唯一灵活重新定义运算符的方式是在 ORM 层中,使用column_property()并提供一个comparator_factory参数。因此,像 GeoAlchemy 这样的第三方库被迫以 ORM 为中心,并依赖各种技巧来应用新操作以及使其正确传播。
核心中的新运算符系统添加了一直缺失的关键点,即将新的和覆盖的运算符与类型关联起来。毕竟,真正驱动操作类型的不是列、CAST 运算符或 SQL 函数,而是表达式的类型。实现细节很少 - 只需向核心ColumnElement类型添加几个额外方法,以便它向其TypeEngine对象查询可选的一组运算符。新的或修订的操作可以与任何类型关联,可以通过对现有类型进行子类化,使用TypeDecorator,或者通过将新的Comparator对象附加到现有类型类来“全面推广”。
例如,要为Numeric类型添加对数支持:
from sqlalchemy.types import Numeric from sqlalchemy.sql import func class CustomNumeric(Numeric): class comparator_factory(Numeric.Comparator): def log(self, other): return func.log(self.expr, other)
这种新类型可以像任何其他类型一样使用:
data = Table( "data", metadata, Column("id", Integer, primary_key=True), Column("x", CustomNumeric(10, 5)), Column("y", CustomNumeric(10, 5)), ) stmt = select([data.c.x.log(data.c.y)]).where(data.c.x.log(2) < value) print(conn.execute(stmt).fetchall())
由此带来的新功能包括立即支持 PostgreSQL 的 HSTORE 类型,以及与 PostgreSQL 的 ARRAY 类型相关的新操作。它还为现有类型开辟了更多特定于这些类型的运算符的道路,例如更多的字符串、整数和日期运算符。
另请参阅
重新定义和创建新运算符
HSTORE
插入的多值支持
Insert.values()方法现在支持字典列表,将生成多 VALUES 语句,如VALUES (), (), ...。这仅适用于支持此语法的后端,包括 PostgreSQL、SQLite 和 MySQL。这与通常的executemany()风格的 INSERT 不同:
users.insert().values( [ {"name": "some name"}, {"name": "some other name"}, {"name": "yet another name"}, ] )
另请参阅
Insert.values()
类型表达式
现在可以将 SQL 表达式与类型关联起来。从历史上看,TypeEngine一直允许 Python 端函数接收绑定参数和结果行值,通过 Python 端转换函数在到达/返回数据库时进行转换。新功能允许类似的功能,但在数据库端进行:
from sqlalchemy.types import String from sqlalchemy import func, Table, Column, MetaData class LowerString(String): def bind_expression(self, bindvalue): return func.lower(bindvalue) def column_expression(self, col): return func.lower(col) metadata = MetaData() test_table = Table("test_table", metadata, Column("data", LowerString))
上面,LowerString类型定义了一个 SQL 表达式,每当test_table.c.data列在 SELECT 语句的列子句中呈现时,该表达式将被发出:
>>> print(select([test_table]).where(test_table.c.data == "HI")) SELECT lower(test_table.data) AS data FROM test_table WHERE test_table.data = lower(:data_1)
这个功能也被新版 GeoAlchemy 大量使用,以根据类型规则在 SQL 中内联嵌入 PostGIS 表达式。
另请参阅
应用 SQL 级别的绑定/结果处理
核心检查系统
inspect()函数引入了新的类/对象检查系统,也适用于核心。应用于Engine会产生一个Inspector对象:
from sqlalchemy import inspect from sqlalchemy import create_engine engine = create_engine("postgresql://scott:tiger@localhost/test") insp = inspect(engine) print(insp.get_table_names())
它也可以应用于任何ClauseElement,它返回ClauseElement本身,比如Table、Column、Select等。这使得它可以在核心和 ORM 构造之间流畅地工作。
新方法Select.correlate_except()
select() 现在有一个方法Select.correlate_except(),指定“除了指定的所有 FROM 子句之外的所有 FROM 子句”。它可用于映射场景,其中相关子查询应该正常关联,除了针对特定目标可选择的情况:
class SnortEvent(Base): __tablename__ = "event" id = Column(Integer, primary_key=True) signature = Column(Integer, ForeignKey("signature.id")) signatures = relationship("Signature", lazy=False) class Signature(Base): __tablename__ = "signature" id = Column(Integer, primary_key=True) sig_count = column_property( select([func.count("*")]) .where(SnortEvent.signature == id) .correlate_except(SnortEvent) )
另请参阅
Select.correlate_except()
PostgreSQL HSTORE 类型
PostgreSQL 的HSTORE类型现在可以作为HSTORE使用。该类型充分利用了新的操作符系统,为 HSTORE 类型提供了一整套操作符,包括索引访问、连接和包含方法,如comparator_factory.has_key()、comparator_factory.has_any()和comparator_factory.matrix():
from sqlalchemy.dialects.postgresql import HSTORE data = Table( "data_table", metadata, Column("id", Integer, primary_key=True), Column("hstore_data", HSTORE), ) engine.execute(select([data.c.hstore_data["some_key"]])).scalar() engine.execute(select([data.c.hstore_data.matrix()])).scalar()
另请参阅
HSTORE
hstore
增强的 PostgreSQL ARRAY 类型
ARRAY 类型将接受一个可选的“维度”参数,将其固定到一个固定数量的维度,大大提高检索结果的效率:
# old way, still works since PG supports N-dimensions per row: Column("my_array", postgresql.ARRAY(Integer)) # new way, will render ARRAY with correct number of [] in DDL, # will process binds and results more efficiently as we don't need # to guess how many levels deep to go Column("my_array", postgresql.ARRAY(Integer, dimensions=2))
该类型还引入了新的操作符,使用新的类型特定的操作符框架。新操作包括索引访问:
result = conn.execute(select([mytable.c.arraycol[2]]))
在 SELECT 中的切片访问:
result = conn.execute(select([mytable.c.arraycol[2:4]]))
在 UPDATE 中的切片更新:
conn.execute(mytable.update().values({mytable.c.arraycol[2:3]: [7, 8]}))
独立的数组文字:
>>> from sqlalchemy.dialects import postgresql >>> conn.scalar(select([postgresql.array([1, 2]) + postgresql.array([3, 4, 5])])) [1, 2, 3, 4, 5]
数组连接,在下面,右侧的[4, 5, 6]被强制转换为数组文字:
select([mytable.c.arraycol + [4, 5, 6]])
另请参阅
ARRAY
array
新的可配置的 SQLite 日期、时间类型
SQLite 没有内置的 DATE、TIME 或 DATETIME 类型,而是提供了一些支持将日期和时间值存储为字符串或整数的方法。0.8 版本中增强了 SQLite 的日期和时间类型,使其更加可配置,包括“微秒”部分是可选的,以及几乎所有其他内容。
Column("sometimestamp", sqlite.DATETIME(truncate_microseconds=True)) Column( "sometimestamp", sqlite.DATETIME( storage_format=( "%(year)04d%(month)02d%(day)02d" "%(hour)02d%(minute)02d%(second)02d%(microsecond)06d" ), regexp="(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{6})", ), ) Column( "somedate", sqlite.DATE( storage_format="%(month)02d/%(day)02d/%(year)04d", regexp="(?P<month>\d+)/(?P<day>\d+)/(?P<year>\d+)", ), )
非常感谢 Nate Dub 在 Pycon 2012 上的努力。
另请参阅
DATETIME
DATE
TIME
“COLLATE”在所有方言中都受支持;特别是 MySQL、PostgreSQL、SQLite
“collate”关键字,长期以来被 MySQL 方言接受,现在已经在所有 String 类型上建立,并且将在任何后端呈现,包括在使用 MetaData.create_all() 和 cast() 等特性时:
>>> stmt = select([cast(sometable.c.somechar, String(20, collation="utf8"))]) >>> print(stmt) SELECT CAST(sometable.somechar AS VARCHAR(20) COLLATE "utf8") AS anon_1 FROM sometable
另见
String
“Prefixes”现在支持于 update(), delete()
面向 MySQL,一个“前缀”可以在这些结构中的任何一个中呈现。例如:
stmt = table.delete().prefix_with("LOW_PRIORITY", dialect="mysql") stmt = table.update().prefix_with("LOW_PRIORITY", dialect="mysql")
该方法是新增的,除了已经存在于 insert(), select() 和 Query 上的方法。
另见
Update.prefix_with()
Delete.prefix_with()
Insert.prefix_with()
Select.prefix_with()
Query.prefix_with()
完全可扩展的核心级别操作符支持
迄今为止,核心从未有过为 Column 和其他表达式构造添加新 SQL 运算符的系统,除了 ColumnOperators.op() 方法,它“刚好足够”使事情正常工作。此外,核心中也从未建立过任何系统,允许覆盖现有运算符的行为。直到现在,灵活重新定义运算符的唯一方法是在 ORM 层中,使用 column_property() 给定一个 comparator_factory 参数。因此,像 GeoAlchemy 这样的第三方库被迫是 ORM 中心的,并且依赖于一系列的黑客来应用新的操作以及使其正确传播。
核心中的新运算符系统添加了一直缺失的一个钩子,即将新的和重写的运算符与类型关联起来。毕竟,真正驱动存在哪些操作的不是列、CAST 运算符或 SQL 函数,而是表达式的类型。实现细节很少——只需向核心 ColumnElement 类型添加几个额外的方法,以便它向其 TypeEngine 对象查询一组可选的运算符。新的或修改后的操作可以与任何类型关联,可以通过对现有类型的子类化、使用 TypeDecorator 或通过将新的 Comparator 对象附加到现有类型类来进行“全面的跨越边界”的关联。
例如,要向 Numeric 类型添加对数支持:
from sqlalchemy.types import Numeric from sqlalchemy.sql import func class CustomNumeric(Numeric): class comparator_factory(Numeric.Comparator): def log(self, other): return func.log(self.expr, other)
新类型可像其他类型一样使用:
data = Table( "data", metadata, Column("id", Integer, primary_key=True), Column("x", CustomNumeric(10, 5)), Column("y", CustomNumeric(10, 5)), ) stmt = select([data.c.x.log(data.c.y)]).where(data.c.x.log(2) < value) print(conn.execute(stmt).fetchall())
此举带来的新功能包括对 PostgreSQL 的 HSTORE 类型的支持,以及与 PostgreSQL 的 ARRAY 类型相关的新操作。它还为现有类型提供了更多专门针对这些类型的操作符的可能性,如更多字符串、整数和日期操作符。
另请参阅
重新定义和创建新运算符
HSTORE
插入的多值支持
Insert.values() 方法现在支持字典列表,这将呈现出多值语句,如 VALUES (), (), ...。这仅与支持此语法的后端相关,包括 PostgreSQL、SQLite 和 MySQL。这与通常的 executemany() 样式的 INSERT 不同:
users.insert().values( [ {"name": "some name"}, {"name": "some other name"}, {"name": "yet another name"}, ] )
另请参阅
Insert.values()
类型表达式
SQL 表达式现在可以与类型关联。在历史上,TypeEngine 一直允许 Python 端函数接收绑定参数和结果行值,并在传递到/从数据库的途中通过 Python 端转换函数进行转换。新功能允许类似的功能,但在数据库端执行:
from sqlalchemy.types import String from sqlalchemy import func, Table, Column, MetaData class LowerString(String): def bind_expression(self, bindvalue): return func.lower(bindvalue) def column_expression(self, col): return func.lower(col) metadata = MetaData() test_table = Table("test_table", metadata, Column("data", LowerString))
上述中,LowerString类型定义了一个 SQL 表达式,每当test_table.c.data列在 SELECT 语句的列子句中被呈现时,该表达式就会被发出:
>>> print(select([test_table]).where(test_table.c.data == "HI")) SELECT lower(test_table.data) AS data FROM test_table WHERE test_table.data = lower(:data_1)
这个特性也被新版的 GeoAlchemy 大量使用,以根据类型规则在 SQL 中内联嵌入 PostGIS 表达式。
另请参阅
应用 SQL 级绑定/结果处理
核心检查系统
引入的inspect()函数新的类/对象检查系统也适用于核心。应用到一个Engine上会产生一个Inspector对象:
from sqlalchemy import inspect from sqlalchemy import create_engine engine = create_engine("postgresql://scott:tiger@localhost/test") insp = inspect(engine) print(insp.get_table_names())
它也可以应用于任何返回自身的ClauseElement,例如Table、Column、Select等。这使它可以在核心和 ORM 构造之间流畅工作。
新方法Select.correlate_except()
select()现在有一个方法Select.correlate_except(),它指定“在除了指定的 FROM 子句之外的所有 FROM 子句上关联”。它可用于映射方案,其中相关子查询应该正常关联,除了针对特定目标可选择的情况:
class SnortEvent(Base): __tablename__ = "event" id = Column(Integer, primary_key=True) signature = Column(Integer, ForeignKey("signature.id")) signatures = relationship("Signature", lazy=False) class Signature(Base): __tablename__ = "signature" id = Column(Integer, primary_key=True) sig_count = column_property( select([func.count("*")]) .where(SnortEvent.signature == id) .correlate_except(SnortEvent) )
另请参阅
Select.correlate_except()
PostgreSQL HSTORE 类型
对 PostgreSQL 的HSTORE类型的支持现在可用作HSTORE。这种类型充分利用了新的运算符系统,为 HSTORE 类型提供了一整套运算符,包括索引访问、连接和包含方法,如comparator_factory.has_key()、comparator_factory.has_any()和comparator_factory.matrix():
from sqlalchemy.dialects.postgresql import HSTORE data = Table( "data_table", metadata, Column("id", Integer, primary_key=True), Column("hstore_data", HSTORE), ) engine.execute(select([data.c.hstore_data["some_key"]])).scalar() engine.execute(select([data.c.hstore_data.matrix()])).scalar()
另请参阅
HSTORE
hstore
增强的 PostgreSQL ARRAY 类型
ARRAY 类型将接受一个可选的“维度”参数,将其固定到一个固定数量的维度,大大提高检索结果的效率:
# old way, still works since PG supports N-dimensions per row: Column("my_array", postgresql.ARRAY(Integer)) # new way, will render ARRAY with correct number of [] in DDL, # will process binds and results more efficiently as we don't need # to guess how many levels deep to go Column("my_array", postgresql.ARRAY(Integer, dimensions=2))
该类型还引入了新的操作符,使用新的类型特定的操作符框架。新操作包括索引访问:
result = conn.execute(select([mytable.c.arraycol[2]]))
在 SELECT 中的切片访问:
result = conn.execute(select([mytable.c.arraycol[2:4]]))
在 UPDATE 中的切片更新:
conn.execute(mytable.update().values({mytable.c.arraycol[2:3]: [7, 8]}))
独立的数组文字:
>>> from sqlalchemy.dialects import postgresql >>> conn.scalar(select([postgresql.array([1, 2]) + postgresql.array([3, 4, 5])])) [1, 2, 3, 4, 5]
数组连接,下面的右侧[4, 5, 6]被强制转换为数组文字:
select([mytable.c.arraycol + [4, 5, 6]])
另请参见
ARRAY
array
新的、可配置的 SQLite 日期、时间类型
SQLite 没有内置的 DATE,TIME 或 DATETIME 类型,而是提供了一些支持,用于将日期和时间值存储为字符串或整数。SQLite 中的日期和时间类型在 0.8 中得到了增强,可以更具体地配置特定格式,包括“微秒”部分是可选的,以及几乎所有其他内容。
Column("sometimestamp", sqlite.DATETIME(truncate_microseconds=True)) Column( "sometimestamp", sqlite.DATETIME( storage_format=( "%(year)04d%(month)02d%(day)02d" "%(hour)02d%(minute)02d%(second)02d%(microsecond)06d" ), regexp="(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{6})", ), ) Column( "somedate", sqlite.DATE( storage_format="%(month)02d/%(day)02d/%(year)04d", regexp="(?P<month>\d+)/(?P<day>\d+)/(?P<year>\d+)", ), )
非常感谢 Nate Dub 在 Pycon 2012 上的努力。
另请参见
DATETIME
DATE
TIME
“COLLATE”在所有方言中都受支持;特别是 MySQL,PostgreSQL,SQLite
“collate”关键字,长期以来被 MySQL 方言接受,现在已在所有String 类型上建立,并将在任何后端上呈现,包括在使用MetaData.create_all()和cast()等功能时:
>>> stmt = select([cast(sometable.c.somechar, String(20, collation="utf8"))]) >>> print(stmt) SELECT CAST(sometable.somechar AS VARCHAR(20) COLLATE "utf8") AS anon_1 FROM sometable
另请参见
String
现在支持“前缀”用于update(), delete()
面向 MySQL,可以在任何这些结构中呈现“前缀”。例如:
stmt = table.delete().prefix_with("LOW_PRIORITY", dialect="mysql") stmt = table.update().prefix_with("LOW_PRIORITY", dialect="mysql")
该方法是新增的,除了已存在于insert()、select()和Query上的方法之外。
另请参阅
Update.prefix_with()
Delete.prefix_with()
Insert.prefix_with()
Select.prefix_with()
Query.prefix_with()
SqlAlchemy 2.0 中文文档(七十八)(5)https://developer.aliyun.com/article/1560970