SqlAlchemy 2.0 中文文档(十)(1)https://developer.aliyun.com/article/1562915
编程或条件版本计数器
当version_id_generator
设置为 False 时,我们还可以以与分配任何其他映射属性相同的方式,在对象上编程(和有条件地)设置版本标识符。例如,如果我们使用了 UUID 示例,但将version_id_generator
设置为False
,我们可以随意设置版本标识符:
import uuid class User(Base): __tablename__ = "user" id = mapped_column(Integer, primary_key=True) version_uuid = mapped_column(String(32), nullable=False) name = mapped_column(String(50), nullable=False) __mapper_args__ = {"version_id_col": version_uuid, "version_id_generator": False} u1 = User(name="u1", version_uuid=uuid.uuid4()) session.add(u1) session.commit() u1.name = "u2" u1.version_uuid = uuid.uuid4() session.commit()
我们也可以在不增加版本计数器的情况下更新我们的User
对象;计数器的值将保持不变,并且 UPDATE 语句仍将针对先前的值进行检查。对于仅某些类别的 UPDATE 对并发问题敏感的方案,这可能很有用:
# will leave version_uuid unchanged u1.name = "u3" session.commit()
简单版本计数
跟踪版本的最直接方法是向映射表添加一个整数列,然后将其设置为映射选项中的version_id_col
:
class User(Base): __tablename__ = "user" id = mapped_column(Integer, primary_key=True) version_id = mapped_column(Integer, nullable=False) name = mapped_column(String(50), nullable=False) __mapper_args__ = {"version_id_col": version_id}
注意
强烈建议将version_id
列设置为 NOT NULL。版本控制功能不支持版本控制列中的 NULL 值。
上面,User
映射使用列version_id
跟踪整数版本。当首次刷新User
类型的对象时,version_id
列的值将为“1”。然后,稍后对表的 UPDATE 将始终以类似以下方式发出:
UPDATE user SET version_id=:version_id, name=:name WHERE user.id = :user_id AND user.version_id = :user_version_id -- {"name": "new name", "version_id": 2, "user_id": 1, "user_version_id": 1}
上述 UPDATE 语句正在更新不仅与 user.id = 1
匹配的行,而且还要求 user.version_id = 1
,其中“1”是我们已知的此对象上一次使用的最后版本标识符。如果其他地方的事务独立修改了行,则此版本 ID 将不再匹配,UPDATE 语句将报告没有匹配的行;这是 SQLAlchemy 测试的条件,确保我们的 UPDATE(或 DELETE)语句仅匹配了一行。如果没有匹配的行,则表示我们的数据版本已过期,并且会引发 StaleDataError
。
自定义版本计数器 / 类型
其他类型的值或计数器可以用于版本控制。常见的类型包括日期和 GUID。当使用替代类型或计数器方案时,SQLAlchemy 提供了一个钩子来使用 version_id_generator
参数,该参数接受版本生成可调用对象。此可调用对象将传递当前已知版本的值,并且预计返回后续版本。
例如,如果我们想要使用随机生成的 GUID 跟踪我们的 User
类的版本控制,我们可以这样做(请注意,一些后端支持原生的 GUID 类型,但我们在这里使用简单的字符串进行演示):
import uuid class User(Base): __tablename__ = "user" id = mapped_column(Integer, primary_key=True) version_uuid = mapped_column(String(32), nullable=False) name = mapped_column(String(50), nullable=False) __mapper_args__ = { "version_id_col": version_uuid, "version_id_generator": lambda version: uuid.uuid4().hex, }
持久性引擎每次将 User
对象受到 INSERT 或 UPDATE 影响时都会调用 uuid.uuid4()
。在这种情况下,我们的版本生成函数可以忽略 version
的传入值,因为 uuid4()
函数生成的标识符没有任何先决条件值。如果我们使用的是顺序版本控制方案,例如数字或特殊字符系统,则可以利用给定的 version
来帮助确定后续值。
另请参阅
不特定后端的 GUID 类型
服务器端版本计数器
version_id_generator
也可以配置为依赖于数据库生成的值。在这种情况下,数据库需要在将行受到 INSERT 时以及 UPDATE 时生成新标识符的某种手段。对于 UPDATE 情况,通常需要一个更新触发器,除非所涉及的数据库支持其他本地版本标识符。特别是,PostgreSQL 数据库支持一个名为 xmin 的系统列,提供 UPDATE 版本控制。我们可以利用 PostgreSQL 的 xmin
列来为我们的 User
类进行版本控制,如下所示:
from sqlalchemy import FetchedValue class User(Base): __tablename__ = "user" id = mapped_column(Integer, primary_key=True) name = mapped_column(String(50), nullable=False) xmin = mapped_column("xmin", String, system=True, server_default=FetchedValue()) __mapper_args__ = {"version_id_col": xmin, "version_id_generator": False}
使用上述映射,ORM 将依赖于 xmin
列来自动提供版本 ID 计数器的新值。
当 ORM 发出 INSERT 或 UPDATE 时,通常不会主动获取数据库生成的值的值,而是将这些列保留为“过期”,并在下次访问它们时获取,除非设置了 eager_defaults
Mapper
标志。然而,当使用服务器端版本列时,ORM 需要主动获取新生成的值。这是为了在任何并发事务可能再次更新它之前设置版本计数器。最好在 INSERT 或 UPDATE 语句中同时进行这个获取,使用 RETURNING,否则,如果之后发出一个 SELECT 语句,那么版本计数器在它被获取之前可能会发生竞争条件。
当目标数据库支持 RETURNING 时,我们的 User
类的 INSERT 语句将如下所示:
INSERT INTO "user" (name) VALUES (%(name)s) RETURNING "user".id, "user".xmin -- {'name': 'ed'}
在上述情况下,ORM 可以在一个语句中获取任何新生成的主键值以及服务器生成的版本标识符。当后端不支持 RETURNING 时,必须为每个 INSERT 和 UPDATE 发出额外的 SELECT,这非常低效,还会引入遗漏版本计数器的可能性:
INSERT INTO "user" (name) VALUES (%(name)s) -- {'name': 'ed'} SELECT "user".version_id AS user_version_id FROM "user" where "user".id = :param_1 -- {"param_1": 1}
仅在绝对必要时,并且仅在支持返回的后端上,强烈建议仅使用服务器端版本计数器,目前支持的后端有 PostgreSQL、Oracle、MariaDB 10.5、SQLite 3.35 和 SQL Server。
编程或有条件的版本计数器
当 version_id_generator
设置为 False 时,我们也可以以编程方式(并有条件地)像分配任何其他映射属性一样,在对象上设置版本标识符。例如,如果我们使用了 UUID 示例,但将 version_id_generator
设置为 False
,我们可以根据自己的需要设置版本标识符:
import uuid class User(Base): __tablename__ = "user" id = mapped_column(Integer, primary_key=True) version_uuid = mapped_column(String(32), nullable=False) name = mapped_column(String(50), nullable=False) __mapper_args__ = {"version_id_col": version_uuid, "version_id_generator": False} u1 = User(name="u1", version_uuid=uuid.uuid4()) session.add(u1) session.commit() u1.name = "u2" u1.version_uuid = uuid.uuid4() session.commit()
我们还可以在不递增版本计数器的情况下更新我们的 User
对象;计数器的值将保持不变,并且 UPDATE 语句仍将根据先前的值进行检查。这在仅特定类的 UPDATE 对并发问题敏感的方案中可能很有用:
# will leave version_uuid unchanged u1.name = "u3" session.commit()
类映射 API
对象名称 | 描述 |
add_mapped_attribute(target, key, attr) | 向 ORM 映射类添加新的映射属性。 |
as_declarative(**kw) | 类装饰器,将给定的类适配为declarative_base() 。 |
class_mapper(class_[, configure]) | 给定一个类,返回与该键关联的主要Mapper 。 |
clear_mappers() | 从所有类中删除所有映射器。 |
column_property(column, *additional_columns, [group, deferred, raiseload, comparator_factory, init, repr, default, default_factory, compare, kw_only, active_history, expire_on_flush, info, doc]) | 为映射提供列级别属性。 |
configure_mappers() | 初始化到目前为止已在所有registry 集合中构造的所有映射器之间的相互关系。 |
declarative_base(*, [metadata, mapper, cls, name, class_registry, type_annotation_map, constructor, metaclass]) | 构造用于声明性类定义的基类。 |
declarative_mixin(cls) | 将类标记为提供“声明混入”功能。 |
DeclarativeBase | 用于声明性类定义的基类。 |
DeclarativeBaseNoMeta | 与DeclarativeBase 相同,但不使用元类拦截新属性。 |
declared_attr | 将类级别方法标记为表示映射属性或声明式指令定义的方法。 |
has_inherited_table(cls) | 给定一个类,如果它继承的任何类都有映射表,则返回 True,否则返回 False。 |
identity_key([class_, ident], *, [instance, row, identity_token]) | 生成“标识键”元组,这些元组用作Session.identity_map 字典中的键。 |
mapped_column([__name_pos, _type_pos], *args, [init, repr, default, default_factory, compare, kw_only, nullable, primary_key, deferred, deferred_group, deferred_raiseload, use_existing_column, name, type, autoincrement, doc, key, index, unique, info, onupdate, insert_default, server_default, server_onupdate, active_history, quote, system, comment, sort_order], **kw) | 在 声明式表 配置中声明一个新的 ORM 映射的 Column 构造。 |
MappedAsDataclass | 用于指示映射此类时,同时将其转换为数据类的混合类。 |
MappedClassProtocol | 表示 SQLAlchemy 映射类的协议。 |
Mapper | 定义 Python 类与数据库表或其他关系结构之间的关联,以便对该类进行的 ORM 操作可以继续进行。 |
object_mapper(instance) | 给定一个对象,返回与对象实例关联的主要 Mapper。 |
orm_insert_sentinel([name, type_], *, [default, omit_from_statements]) | 提供一个替代 mapped_column() 的代理,生成所谓的 sentinel 列,允许在其他情况下没有符合条件的主键配置的表中进行高效的批量插入,并且具有确定性的 RETURNING 排序。 |
polymorphic_union(table_map, typecolname[, aliasname, cast_nulls]) | 创建多态映射器使用的 UNION 语句。 |
reconstructor(fn) | 将方法装饰为 ‘reconstructor’ 钩子。 |
注册表 | 用于映射类的通用注册表。 |
synonym_for(name[, map_column]) | 与 Python 描述符一起生成一个 synonym() 属性的装饰器。 |
class sqlalchemy.orm.registry
用于映射类的通用注册表。
registry
用作维护映射集合的基础,并提供用于映射类的配置钩子。
支持的三种常规映射类型是声明基类(Declarative Base)、声明装饰器(Declarative Decorator)和命令式映射(Imperative Mapping)。所有这些映射样式都可以互换使用:
registry.generate_base()
返回一个新的声明基类,是declarative_base()
函数的底层实现。registry.mapped()
提供了一个类装饰器,它将为一个类应用声明性映射,而不使用声明性基类。registry.map_imperatively()
会为一个类生成一个Mapper
,而不会扫描该类以寻找声明性类属性。这种方法适用于历史上由sqlalchemy.orm.mapper()
传统映射函数提供的用例,该函数已在 SQLAlchemy 2.0 中移除。
从版本 1.4 新增。
成员
init(), as_declarative_base(), configure(), dispose(), generate_base(), map_declaratively(), map_imperatively(), mapped(), mapped_as_dataclass(), mappers, update_type_annotation_map()
参见
ORM 映射类概述 - 类映射样式概述。
method __init__(*, metadata: Optional[MetaData] = None, class_registry: Optional[clsregistry._ClsRegistryType] = None, type_annotation_map: Optional[_TypeAnnotationMapType] = None, constructor: Callable[..., None] = <function _declarative_constructor>)
构建一个新的 registry
参数:
metadata
– 一个可选的MetaData
实例。使用声明性表映射生成的所有Table
对象将使用此MetaData
集合。如果将此参数保留在默认值None
,则会创建一个空白的MetaData
集合。constructor
– 指定在没有自己的__init__
的映射类上的__init__
函数的实现。默认情况下,为声明的字段和关系分配 **kwargs 的实现分配给一个实例。如果提供None
,则不会提供 init,并且构造将回退到 cls.init 的普通 Python 语义。class_registry
– 可选的字典,当使用字符串名称来标识relationship()
等内部类时,将充当类名称->映射类的注册表。允许两个或更多声明性基类共享相同的类名称注册表,以简化基类之间的关系。type_annotation_map
–
可选的 Python 类型到 SQLAlchemyTypeEngine
类或实例的字典。提供的字典将更新默认类型映射。这仅由MappedColumn
构造在Mapped
类型内部的注解产生列类型时使用。
新版本 2.0 中的内容。
另请参阅
自定义类型映射
method as_declarative_base(**kw: Any) → Callable[[Type[_T]], Type[_T]]
类装饰器,将为给定的基类调用registry.generate_base()
。
例如:
from sqlalchemy.orm import registry mapper_registry = registry() @mapper_registry.as_declarative_base() class Base: @declared_attr def __tablename__(cls): return cls.__name__.lower() id = Column(Integer, primary_key=True) class MyMappedClass(Base): # ...
所有传递给registry.as_declarative_base()
的关键字参数都会传递给registry.generate_base()
。
method configure(cascade: bool = False) → None
配置此注册表
中所有尚未配置的映射器。
配置步骤用于调和和初始化relationship()
链接,以及调用配置事件,如MapperEvents.before_configured()
和MapperEvents.after_configured()
,这些事件可以被 ORM 扩展或用户定义的扩展钩子所使用。
如果此注册表中的一个或多个映射器包含指向其他注册表中映射类的relationship()
构造,则称该注册表为依赖于那些注册表。为了自动配置这些依赖注册表,configure.cascade
标志应设置为True
。否则,如果它们未配置,则会引发异常。此行为背后的原理是允许应用程序在控制是否隐式到达其他注册表的同时,以编程方式调用注册表的配置。
作为调用registry.configure()
的替代方案,可以使用 ORM 函数configure_mappers()
函数确保内存中所有registry
对象的配置完成。这通常更简单,并且还早于整体使用registry
对象的用法。但是,此函数将影响运行中的 Python 进程中的所有映射,并且对于具有许多用于不同目的的注册表的应用程序可能更耗费内存/时间,这些注册表可能不会立即需要。
另请参阅
configure_mappers()
自版本 1.4.0b2 新增。
method dispose(cascade: bool = False) → None
处理此 registry
中的所有映射器。
调用后,此注册表中映射的所有类将不再具有与类相关联的类仪器。该方法是每个registry
的类似于应用程序范围的clear_mappers()
函数。
如果此注册表包含其他注册表的依赖项映射器,通常通过relationship()
链接,则必须将这些注册表也处理掉。当这些注册表存在于与此相关的关系中时,如果设置了dispose.cascade
标志为True
,则它们的registry.dispose()
方法也将被调用;否则,如果这些注册表尚未被处理,则会引发错误。
自版本 1.4.0b2 新增。
另请参阅
clear_mappers()
method generate_base(mapper: ~typing.Callable[[...], ~sqlalchemy.orm.mapper.Mapper[~typing.Any]] | None = None, cls: ~typing.Type[~typing.Any] = <class 'object'>, name: str = 'Base', metaclass: ~typing.Type[~typing.Any] = <class 'sqlalchemy.orm.decl_api.DeclarativeMeta'>) → Any
生成一个声明性基类。
继承自返回的类对象的类将使用声明性映射自动映射。
例如:
from sqlalchemy.orm import registry mapper_registry = registry() Base = mapper_registry.generate_base() class MyClass(Base): __tablename__ = "my_table" id = Column(Integer, primary_key=True)
上述动态生成的类等同于下面的非动态示例:
from sqlalchemy.orm import registry from sqlalchemy.orm.decl_api import DeclarativeMeta mapper_registry = registry() class Base(metaclass=DeclarativeMeta): __abstract__ = True registry = mapper_registry metadata = mapper_registry.metadata __init__ = mapper_registry.constructor
自版本 2.0 变更:请注意,registry.generate_base()
方法已被新的DeclarativeBase
类取代,该类使用子类化生成一个新的“基”类,而不是函数的返回值。这样可以与PEP 484类型工具兼容的方法。
registry.generate_base()
方法提供了declarative_base()
函数的实现,该函数一次性创建了registry
和基类。
查看声明式映射部分以获取背景和示例。
参数:
mapper
– 可选可调用对象,默认为Mapper
。此函数用于生成新的Mapper
对象。cls
– 默认为object
。要用作生成的声明性基类的基础的类型。可以是类或类的元组。name
– 默认为Base
。生成类的显示名称。虽然不需要自定义此项,但可以提高回溯和调试时的清晰度。metaclass
– 默认为DeclarativeMeta
。作为生成的声明性基类的元类型的元类或__metaclass__
兼容可调用对象。
另请参阅
声明式映射
declarative_base()
method map_declaratively(cls: Type[_O]) → Mapper[_O]
声明性地映射一个类。
在这种映射形式中,将扫描类以获取映射信息,包括要与表关联的列和/或实际表对象。
返回Mapper
对象。
例如:
from sqlalchemy.orm import registry mapper_registry = registry() class Foo: __tablename__ = 'some_table' id = Column(Integer, primary_key=True) name = Column(String) mapper = mapper_registry.map_declaratively(Foo)
此函数更方便地通过registry.mapped()
类装饰器或通过从registry.generate_base()
生成的声明性元类的子类间接调用。
查看完整详情和示例,请参阅声明式映射部分。
SqlAlchemy 2.0 中文文档(十)(3)https://developer.aliyun.com/article/1562917