SqlAlchemy 2.0 中文文档(三十三)(4)https://developer.aliyun.com/article/1562718
版本化对象
带有历史表的版本控制
演示了一个扩展,为实体创建版本表并存储每次更改的记录。给定的扩展生成一个匿名的“历史”类,表示目标对象的历史版本。
与使用时间行进行版本控制示例进行比较,该示例将更新写入为同一表中的新行,而不使用单独的历史表。
使用通过一个单元测试模块test_versioning.py
进行演示,可以像其他模块一样运行,内部使用unittest
:
python -m examples.versioned_history.test_versioning
一个示例用法片段,使用声明性:
from history_meta import Versioned, versioned_session class Base(DeclarativeBase): pass class SomeClass(Versioned, Base): __tablename__ = 'sometable' id = Column(Integer, primary_key=True) name = Column(String(50)) def __eq__(self, other): assert type(other) is SomeClass and other.id == self.id Session = sessionmaker(bind=engine) versioned_session(Session) sess = Session() sc = SomeClass(name='sc1') sess.add(sc) sess.commit() sc.name = 'sc1modified' sess.commit() assert sc.version == 2 SomeClassHistory = SomeClass.__history_mapper__.class_ assert sess.query(SomeClassHistory).\ filter(SomeClassHistory.version == 1).\ all() \ == [SomeClassHistory(version=1, name='sc1')]
Versioned
混合类设计用于与声明性一起使用。要将扩展与经典映射器一起使用,可以应用_history_mapper
函数:
from history_meta import _history_mapper m = mapper(SomeClass, sometable) _history_mapper(m) SomeHistoryClass = SomeClass.__history_mapper__.class_
版本示例还与 ORM 乐观并发功能集成,文档化在配置版本计数器。要启用此功能,请将标志Versioned.use_mapper_versioning
设置为 True:
class SomeClass(Versioned, Base): __tablename__ = 'sometable' use_mapper_versioning = True id = Column(Integer, primary_key=True) name = Column(String(50)) def __eq__(self, other): assert type(other) is SomeClass and other.id == self.id
如果两个具有相同版本标识符的SomeClass
实例被同时更新并发送到数据库进行并发更新,如果数据库隔离级别允许两个 UPDATE 语句继续进行,其中一个将失败,因为它不再针对最后已知的版本标识符。
文件清单:
- test_versioning.py - 展示了
history_meta.py
模块函数的使用的单元测试。 - history_meta.py - 带有版本控制的混合类和其他实用工具。#### 使用时间行进行版本控制
几个示例说明拦截更改的技术,这些更改首先被解释为对行的更新,而实际上将其转换为对新行的插入,保留先前的行作为历史版本。
与带有历史表的版本控制示例进行比较,该示例将历史行写入单独的历史表。
文件清单:
- versioned_rows.py - 说明了拦截对象更改的方法,将对单个行的 UPDATE 语句转换为 INSERT 语句,以便使用新数据插入新行,保留旧行不变。
- versioned_rows_w_versionid.py - 说明了拦截对象更改的方法,将对单个行的 UPDATE 语句转换为 INSERT 语句,以便使用新数据插入新行,保留旧行不变。
- versioned_map.py - 围绕“垂直表”结构的概念构建的
versioned_rows
示例的变体,类似于垂直属性映射示例中所示的内容。 - versioned_update_old_row.py - 说明了
versioned_rows.py
的相同 UPDATE 到 INSERT 技术,但还会对旧行进行 UPDATE 以影响时间戳的更改。还包括一个SessionEvents.do_orm_execute()
挂钩来将查询限制为只有最新版本。 #### 使用历史表进行版本控制
展示了一个创建实体的版本表并为每个更改存储记录的扩展。给定的扩展生成一个匿名的“history”类,表示目标对象的历史版本。
与使用时间行版本化的例子相比,这些例子将更新写入相同表中的新行中,而不使用单独的历史表。
通过一个单元测试模块test_versioning.py
来说明使用方法,可以像其他模块一样运行,内部使用unittest
:
python -m examples.versioned_history.test_versioning
一个示例用法片段,使用声明式:
from history_meta import Versioned, versioned_session class Base(DeclarativeBase): pass class SomeClass(Versioned, Base): __tablename__ = 'sometable' id = Column(Integer, primary_key=True) name = Column(String(50)) def __eq__(self, other): assert type(other) is SomeClass and other.id == self.id Session = sessionmaker(bind=engine) versioned_session(Session) sess = Session() sc = SomeClass(name='sc1') sess.add(sc) sess.commit() sc.name = 'sc1modified' sess.commit() assert sc.version == 2 SomeClassHistory = SomeClass.__history_mapper__.class_ assert sess.query(SomeClassHistory).\ filter(SomeClassHistory.version == 1).\ all() \ == [SomeClassHistory(version=1, name='sc1')]
Versioned
mixin 设计为与声明性一起使用。要将扩展与经典映射器一起使用,可以应用_history_mapper
函数:
from history_meta import _history_mapper m = mapper(SomeClass, sometable) _history_mapper(m) SomeHistoryClass = SomeClass.__history_mapper__.class_
版本控制示例还与在配置版本计数器中记录的 ORM 乐观并发功能集成。要启用此功能,请将标志Versioned.use_mapper_versioning
设置为 True:
class SomeClass(Versioned, Base): __tablename__ = 'sometable' use_mapper_versioning = True id = Column(Integer, primary_key=True) name = Column(String(50)) def __eq__(self, other): assert type(other) is SomeClass and other.id == self.id
上面,如果两个具有相同版本标识符的SomeClass
实例被同时更新并发送到数据库进行并发更新,如果数据库隔离级别允许这两个 UPDATE 语句继续进行,其中一个将失败,因为它不再是针对最后已知版本标识符的。
文件列表:
- test_versioning.py - 展示了
history_meta.py
模块函数的用法的单元测试。 - history_meta.py - 版本混合类和其他实用工具。
使用时间行版本化
几个示例说明了拦截更改的技术,这些更改首先被解释为对行的 UPDATE,而实际上将其转换为对新行的 INSERT,将先前的行保留为历史版本。
与带有历史表的版本控制示例进行比较,该示例将历史行写入单独的历史表中。
文件列表:
- versioned_rows.py - 展示了拦截对象更改的方法,将对单行的 UPDATE 语句转换为 INSERT 语句,以便插入具有新数据的新行,同时保留旧行不变。
- versioned_rows_w_versionid.py - 展示了拦截对象更改的方法,将对单行的 UPDATE 语句转换为 INSERT 语句,以便插入具有新数据的新行,同时保留旧行不变。
- versioned_map.py - 围绕“垂直表”结构概念构建的 versioned_rows 示例的变体,类似于垂直属性映射示例中所示的那些。
- versioned_update_old_row.py - 展示了与
versioned_rows.py
相同的 UPDATE 转换为 INSERT 技术,但还发出了对旧行的 UPDATE 以影响时间戳的更改。还包括一个SessionEvents.do_orm_execute()
钩子,以限制查询仅针对最新版本。
垂直属性映射
展示了“垂直表”映射。
“垂直表”是指将对象的各个属性存储为表中的不同行的技术。 “垂直表”技术用于持久化可以具有各种属性集的对象,但牺牲了简单的查询控制和简洁性。它通常在内容/文档管理系统中找到,以灵活地表示用户创建的结构。
给出了两种方法的变体。在第二种方法中,每行引用一个包含有关存储在属性中的信息类型的“数据类型”,例如整数、字符串或日期。
示例:
shrew = Animal(u'shrew') shrew[u'cuteness'] = 5 shrew[u'weasel-like'] = False shrew[u'poisonous'] = True session.add(shrew) session.flush() q = (session.query(Animal). filter(Animal.facts.any( and_(AnimalFact.key == u'weasel-like', AnimalFact.value == True)))) print('weasel-like animals', q.all())
文件列表:
- dictlike-polymorphic.py - 将多态值的垂直表映射为字典。
- dictlike.py - 将垂直表映射为字典。
继承映射配方
基本继承映射
单表、联表和具体表继承的工作示例,如映射类继承层次结构中所述。
文件列表:
- joined.py - 联接表(每个子类一个表)继承示例。
- concrete.py - 具体表(每类一表)继承示例。
- single.py - 单表(按层次结构划分)继承示例。 ### 基本继承映射
作为 Mapping Class Inheritance Hierarchies 中描述的单表、连接表和具体表继承的工作示例。
文件清单:
- joined.py - 连接表(每子类一表)继承示例。
- concrete.py - 具体表(每类一表)继承示例。
- single.py - 单表(按层次结构划分)继承示例。
特殊 API
属性仪器化
示例说明了对 SQLAlchemy 属性管理系统的修改。
文件清单:
- listen_for_events.py - 演示了如何将事件附加到所有仪器化属性并监听更改事件。
- active_column_defaults.py - 演示了如何使用
AttributeEvents.init_scalar()
事件,结合核心列默认值来提供 ORM 对象,当访问未设置的属性时自动产生默认值。 - custom_management.py - 演示了如何使用
sqlalchemy.ext.instrumentation
扩展包进行自定义类仪器化。 ### 水平分片
使用 SQLAlchemy 分片 API 的基本示例。分片是指将数据横向扩展到多个数据库。
“分片”映射的基本组件包括:
- 多个
Engine
实例,每个实例分配一个“分片 ID”。这些Engine
实例可以引用不同的数据库,或者同一数据库中的不同模式/帐户,或者甚至可以仅通过选项来区分,当使用时会使它们访问不同的模式或表。 - 给定要保存的实例,可以返回单个分片 ID 的函数;这称为“shard_chooser”。
- 可以返回适用于特定实例标识符的分片 ID 列表的函数;这称为“id_chooser”。如果返回所有分片 ID,则会搜索所有分片。
- 给定特定查询(“query_chooser”),可以返回要尝试的分片 ID 列表的函数。如果返回所有分片 ID,则会查询所有分片并将结果合并在一起。
在这些示例中,针对相同的基本示例使用不同类型的分片,适应以每个大陆为基础的天气数据。我们提供了示例的 shard_chooser、id_chooser 和 query_chooser 函数。query_chooser 演示了检查 SQL 表达式元素以尝试确定请求的单个分片。
构建通用分片例程是解决将实例组织在多个数据库中的问题的一种雄心勃勃的方法。对于更直接的替代方案,“不同实体”方法是一种简单的将对象分配给不同表(以及潜在的数据库节点)的显式方法 - 在维基上描述为EntityName。
文件列表:
- separate_databases.py - 演示使用不同的 SQLite 数据库进行分片。
- separate_tables.py - 演示使用单个 SQLite 数据库进行分片,但将使用命名约定创建多个表。
- separate_schema_translates.py - 演示使用具有多个模式的单个数据库进行分片,其中可以为每个分片使用不同的“schema_translates_map”。
- asyncio.py - 演示与 asyncio 一起使用分片 API。### 属性检测
演示对 SQLAlchemy 属性管理系统的修改的示例。
文件列表:
- listen_for_events.py - 演示如何将事件附加到所有被检测属性并监听更改事件。
- active_column_defaults.py - 演示使用
AttributeEvents.init_scalar()
事件,结合核心列默认值,为 ORM 对象提供在访问未设置属性时自动生成默认值的功能。 - custom_management.py - 演示自定义类检测,使用
sqlalchemy.ext.instrumentation
扩展包。
水平分片
使用 SQLAlchemy 分片 API 的基本示例。分片是指在多个数据库之间水平扩展数据。
“分片”映射的基本组件包括:
- 多个
Engine
实例,每个分配一个“分片 id”。这些Engine
实例可以引用不同的数据库,或者同一数据库中的不同模式/帐户,或者它们甚至可以仅通过选项进行区分,这些选项将在使用时导致它们访问不同的模式或表。 - 一个函数可以返回给定要保存的实例的单个分片 id;这称为“shard_chooser”。
- 一个函数可以返回适用于特定实例标识符的分片 id 列表;这称为“id_chooser”。如果返回所有分片 id,则将搜索所有分片。
- 一个函数可以返回给定查询的尝试分片 id 列表;如果返回所有分片 id,则将查询所有分片并将结果连接在一起。
在这些示例中,针对同一基本示例使用了不同类型的分片,该示例适用于按大陆基础提供天气数据。我们提供了示例 shard_chooser、id_chooser 和 query_chooser 函数。query_chooser 展示了对 SQL 表达式元素的检查,以尝试确定请求的单个分片。
构建通用分片例程是组织实例在多个数据库中的一种雄心勃勃的方法。对于更直接的替代方案,“不同实体”方法是一种将对象分配给不同表(和潜在的数据库节点)的简单方法 - 在维基上描述为EntityName。
文件列表:
- separate_databases.py - 展示了使用不同的 SQLite 数据库进行分片的示例。
- separate_tables.py - 展示了使用单个 SQLite 数据库进行分片的示例,但是会使用多个表并遵循命名约定。
- separate_schema_translates.py - 展示了使用具有多个模式的单个数据库进行分片的示例,其中每个分片可以使用不同的“schema_translates_map”。
- asyncio.py - 展示了与 asyncio 一起使用的分片 API。
扩展 ORM
ORM 查询事件
展示了增强 ORM SELECT 行为的示例,这些示例由 Session.execute()
与 2.0 风格 的 select()
以及 1.x 风格 的 Query
对象一起使用。
示例包括演示with_loader_criteria()
选项以及SessionEvents.do_orm_execute()
钩子。
截至 SQLAlchemy 1.4 版本,Query
构造与Select
构造合并在一起,因此这两个对象大部分相同。
文件列表:
- temporal_range.py - 演示将应用于选定实体的自定义每个查询条件。
- filter_public.py - 演示应用于特定类型实体的全局条件的基本技术。### Dogpile Caching
演示如何嵌入dogpile.cache功能与 ORM 查询,允许完全控制缓存以及从长期缓存中获取“延迟加载”属性的能力。
在这个演示中,演示了以下技术:
- 使用
SessionEvents.do_orm_execute()
事件钩子 - 绕过
Session.execute()
以从自定义缓存源而不是数据库中提取的基本技术。 - 使用 dogpile.cache 进行基本缓存,使用“regions”允许全局控制一组固定配置。
- 使用自定义
UserDefinedOption
对象配置语句对象中的选项。
另请参阅
重新执行语句 - 包括这里介绍的技术的一个通用示例。
例如:
# query for Person objects, specifying cache stmt = select(Person).options(FromCache("default")) # specify that each Person's "addresses" collection comes from # cache too stmt = stmt.options(RelationshipCache(Person.addresses, "default")) # execute and results result = session.execute(stmt) print(result.scalars().all())
要运行,必须安装 SQLAlchemy 和 dogpile.cache,或者在当前 PYTHONPATH 上。演示将为数据文件创建一个本地目录,插入初始数据,然后运行。第二次运行演示将利用已经存在的缓存文件,并且只会发出一条 SQL 语句针对两个表 - 但显示的结果将利用数十个从缓存中获取的延迟加载。
演示脚本本身,按复杂性顺序作为 Python 模块运行,以便相对导入正常工作:
python -m examples.dogpile_caching.helloworld python -m examples.dogpile_caching.relationship_caching python -m examples.dogpile_caching.advanced python -m examples.dogpile_caching.local_session_caching
文件列表:
- environment.py - 建立数据/缓存文件路径和配置,必要时引导固定数据。
- caching_query.py - 代表允许在 SQLAlchemy 中使用 Dogpile 缓存的函数和类。引入一个名为 FromCache 的查询选项。
- model.py - 数据模型,表示具有多个地址对象的人员,每个地址对象都有邮政编码、城市和国家。
- fixture_data.py - 安装一些示例数据。这里有一些美国/加拿大城市的少数邮政编码。然后,安装了 100 个人员记录,每个记录都有一个随机选择的邮政编码。
- helloworld.py - 演示了如何加载一些数据,并缓存结果。
- relationship_caching.py - 展示了如何在关系终点添加缓存选项,以便惰性加载从缓存中加载。
- advanced.py - 演示了 Query 与 FromCache 选项的使用,包括前端加载、缓存失效和集合缓存。
- local_session_caching.py - 这个例子创建了一个新的 dogpile.cache 后端,它将数据持久化存储在当前会话的字典中。删除会话,缓存就消失了。### ORM 查询事件
说明如何使用Session.execute()
与 2.0 样式的select()
一起增强 ORM SELECT 行为的示例,以及 1.x 样式的Query
对象。
示例包括演示with_loader_criteria()
选项以及SessionEvents.do_orm_execute()
钩子。
从 SQLAlchemy 1.4 开始,Query
构造与Select
构造统一,因此这两个对象大部分相同。
文件列表:
- temporal_range.py - 展示了将应用于选定实体的自定义每个查询条件。
- filter_public.py - 展示了应用于特定类型实体的全局条件。
Dogpile 缓存
说明如何在 ORM 查询中嵌入dogpile.cache功能,允许完全的缓存控制,以及从长期缓存中拉取“惰性加载”属性的能力。
这个演示展示了以下技术:
- 使用
SessionEvents.do_orm_execute()
事件挂钩 - 绕过
Session.execute()
以从自定义缓存源而不是数据库中提取的基本技术。 - 基本的缓存与 dogpile.cache,使用允许全局控制一组固定配置的“区域”。
- 使用自定义
UserDefinedOption
对象配置语句对象中的选项。
另请参阅
重新执行语句 - 包括这里介绍的技术的一般示例。
例如:
# query for Person objects, specifying cache stmt = select(Person).options(FromCache("default")) # specify that each Person's "addresses" collection comes from # cache too stmt = stmt.options(RelationshipCache(Person.addresses, "default")) # execute and results result = session.execute(stmt) print(result.scalars().all())
要运行,必须安装 SQLAlchemy 和 dogpile.cache,或者在当前 PYTHONPATH 上安装。演示将为数据文件创建一个本地目录,插入初始数据,并运行。再次运行演示将利用已经存在的缓存文件,并且只会发出一条针对两个表的 SQL 语句 - 但显示的结果将利用几十个懒加载,所有这些懒加载都从缓存中拉取。
演示脚本本身,按复杂度顺序运行为 Python 模块,以便相对导入正常工作:
python -m examples.dogpile_caching.helloworld python -m examples.dogpile_caching.relationship_caching python -m examples.dogpile_caching.advanced python -m examples.dogpile_caching.local_session_caching
文件清单:
- environment.py - 确定数据/缓存文件路径和配置,如有必要,引导安装装置数据。
- caching_query.py - 表示使用 SQLAlchemy 可以与 Dogpile 缓存一起使用的函数和类。介绍了一个名为 FromCache 的查询选项。
- model.py - 数据模型,表示具有多个 Address 对象的 Person,每个 Address 对象都有 PostalCode、City、Country。
- fixture_data.py - 安装一些示例数据。这里有一些美国/加拿大几个城市的一些邮政编码。然后,安装了 100 个 Person 记录,每个记录都有一个随机选择的邮政编码。
- helloworld.py - 演示如何加载一些数据,并缓存结果。
- relationship_caching.py - 演示如何在关系端点上添加缓存选项,以便延迟加载从缓存加载。
- advanced.py - 演示 Query 与 FromCache 选项的使用,包括前端加载、缓存失效和集合缓存。
- local_session_caching.py - 此示例创建一个新的 dogpile.cache 后端,该后端将数据持久化在当前会话的字典中。删除会话,缓存消失。
s.dogpile_caching.relationship_caching
python -m examples.dogpile_caching.advanced
python -m examples.dogpile_caching.local_session_caching
文件列表: + environment.py - 建立数据/缓存文件路径和配置,必要时引导固定数据。 + caching_query.py - 代表允许在 SQLAlchemy 中使用 Dogpile 缓存的函数和类。引入一个名为 FromCache 的查询选项。 + model.py - 数据模型,表示具有多个地址对象的人员,每个地址对象都有邮政编码、城市和国家。 + fixture_data.py - 安装一些示例数据。这里有一些美国/加拿大城市的少数邮政编码。然后,安装了 100 个人员记录,每个记录都有一个随机选择的邮政编码。 + helloworld.py - 演示了如何加载一些数据,并缓存结果。 + relationship_caching.py - 展示了如何在关系终点添加缓存选项,以便惰性加载从缓存中加载。 + advanced.py - 演示了 Query 与 FromCache 选项的使用,包括前端加载、缓存失效和集合缓存。 + local_session_caching.py - 这个例子创建了一个新的 dogpile.cache 后端,它将数据持久化存储在当前会话的字典中。删除会话,缓存就消失了。### ORM 查询事件 说明如何使用`Session.execute()`与 2.0 样式的`select()`一起增强 ORM SELECT 行为的示例,以及 1.x 样式的`Query`对象。 示例包括演示`with_loader_criteria()`选项以及`SessionEvents.do_orm_execute()`钩子。 从 SQLAlchemy 1.4 开始,`Query`构造与`Select`构造统一,因此这两个对象大部分相同。 文件列表: + temporal_range.py - 展示了将应用于选定实体的自定义每个查询条件。 + filter_public.py - 展示了应用于特定类型实体的全局条件。 ### Dogpile 缓存 说明如何在 ORM 查询中嵌入[dogpile.cache](https://dogpilecache.sqlalchemy.org/)功能,允许完全的缓存控制,以及从长期缓存中拉取“惰性加载”属性的能力。 这个演示展示了以下技术: + 使用`SessionEvents.do_orm_execute()`事件挂钩 + 绕过`Session.execute()`以从自定义缓存源而不是数据库中提取的基本技术。 + 基本的缓存与 dogpile.cache,使用允许全局控制一组固定配置的“区域”。 + 使用自定义`UserDefinedOption`对象配置语句对象中的选项。 另请参阅 重新执行语句 - 包括这里介绍的技术的一般示例。 例如: ```py # query for Person objects, specifying cache stmt = select(Person).options(FromCache("default")) # specify that each Person's "addresses" collection comes from # cache too stmt = stmt.options(RelationshipCache(Person.addresses, "default")) # execute and results result = session.execute(stmt) print(result.scalars().all())
要运行,必须安装 SQLAlchemy 和 dogpile.cache,或者在当前 PYTHONPATH 上安装。演示将为数据文件创建一个本地目录,插入初始数据,并运行。再次运行演示将利用已经存在的缓存文件,并且只会发出一条针对两个表的 SQL 语句 - 但显示的结果将利用几十个懒加载,所有这些懒加载都从缓存中拉取。
演示脚本本身,按复杂度顺序运行为 Python 模块,以便相对导入正常工作:
python -m examples.dogpile_caching.helloworld python -m examples.dogpile_caching.relationship_caching python -m examples.dogpile_caching.advanced python -m examples.dogpile_caching.local_session_caching
文件清单:
- environment.py - 确定数据/缓存文件路径和配置,如有必要,引导安装装置数据。
- caching_query.py - 表示使用 SQLAlchemy 可以与 Dogpile 缓存一起使用的函数和类。介绍了一个名为 FromCache 的查询选项。
- model.py - 数据模型,表示具有多个 Address 对象的 Person,每个 Address 对象都有 PostalCode、City、Country。
- fixture_data.py - 安装一些示例数据。这里有一些美国/加拿大几个城市的一些邮政编码。然后,安装了 100 个 Person 记录,每个记录都有一个随机选择的邮政编码。
- helloworld.py - 演示如何加载一些数据,并缓存结果。
- relationship_caching.py - 演示如何在关系端点上添加缓存选项,以便延迟加载从缓存加载。
- advanced.py - 演示 Query 与 FromCache 选项的使用,包括前端加载、缓存失效和集合缓存。
- local_session_caching.py - 此示例创建一个新的 dogpile.cache 后端,该后端将数据持久化在当前会话的字典中。删除会话,缓存消失。