SqlAlchemy 2.0 中文文档(三十一)

2024-06-26 15:34:13 浏览数 (1)

原文:docs.sqlalchemy.org/en/20/contents.html

声明式扩展

原文:docs.sqlalchemy.org/en/20/orm/extensions/declarative/index.html

声明式映射 API 特定的扩展。

1.4 版本更改:绝大部分声明式扩展现在已整合到 SQLAlchemy ORM 中,并可从 sqlalchemy.orm 命名空间导入。请参阅声明式映射的文档以获取新文档。有关更改的概述,请参阅声明式现已与 ORM 整合,并带有新功能。

对象名称

描述

AbstractConcreteBase

一个用于“具体”声明式映射的辅助类。

ConcreteBase

一个用于“具体”声明式映射的辅助类。

DeferredReflection

一个用于基于延迟反射步骤构建映射的辅助类。

代码语言:javascript复制
class sqlalchemy.ext.declarative.AbstractConcreteBase

一个用于“具体”声明式映射的辅助类。

AbstractConcreteBase 将自动使用 polymorphic_union() 函数,对所有作为此类的子类映射的表执行。该函数通过 __declare_first__() 函数调用,这实际上是一个 before_configured() 事件的钩子。

AbstractConcreteBase 应用 Mapper 到其直接继承的类,就像对任何其他声明式映射的类一样。然而,Mapper 没有映射到任何特定的 Table 对象。相反,它直接映射到由 polymorphic_union() 产生的“多态”可选择的对象,并且不执行自己的持久化操作。与 ConcreteBase 相比,后者将其直接继承的类映射到直接存储行的实际 Table

注意

AbstractConcreteBase延迟了基类的映射器创建,直到所有子类都已定义,因为它需要创建一个针对包含所有子类表的可选择项的映射。为了实现这一点,它等待映射器配置事件发生,然后扫描所有配置的子类,并设置一个将一次性查询所有子类的映射。

虽然此事件通常会自动调用,但在AbstractConcreteBase的情况下,如果第一个操作是针对此基类的查询,则可能需要在定义所有子类映射之后显式调用它。为此,一旦所有期望的类都已配置,可以调用正在使用的registry上的registry.configure()方法,该方法可在特定声明基类的关系中使用:

代码语言:javascript复制
Base.registry.configure()

示例:

代码语言:javascript复制
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.ext.declarative import AbstractConcreteBase

class Base(DeclarativeBase):
    pass

class Employee(AbstractConcreteBase, Base):
    pass

class Manager(Employee):
    __tablename__ = 'manager'
    employee_id = Column(Integer, primary_key=True)
    name = Column(String(50))
    manager_data = Column(String(40))

    __mapper_args__ = {
        'polymorphic_identity':'manager',
        'concrete':True
    }

Base.registry.configure()

抽象基类在声明时以一种特殊的方式处理;在类配置时,它的行为类似于声明式的混入或__abstract__基类。一旦类被配置并生成映射,它会被映射自身,但在其所有子类之后。这是在任何其他 SQLAlchemy API 功能中都找不到的非常独特的映射系统。

使用这种方法,我们可以指定将在映射的子类上发生的列和属性,就像我们通常在 Mixin 和自定义基类中所做的那样:

代码语言:javascript复制
from sqlalchemy.ext.declarative import AbstractConcreteBase

class Company(Base):
    __tablename__ = 'company'
    id = Column(Integer, primary_key=True)

class Employee(AbstractConcreteBase, Base):
    strict_attrs = True

    employee_id = Column(Integer, primary_key=True)

    @declared_attr
    def company_id(cls):
        return Column(ForeignKey('company.id'))

    @declared_attr
    def company(cls):
        return relationship("Company")

class Manager(Employee):
    __tablename__ = 'manager'

    name = Column(String(50))
    manager_data = Column(String(40))

    __mapper_args__ = {
        'polymorphic_identity':'manager',
        'concrete':True
    }

Base.registry.configure()

然而,当我们使用我们的映射时,ManagerEmployee都将拥有一个可独立使用的.company属性:

代码语言:javascript复制
session.execute(
    select(Employee).filter(Employee.company.has(id=5))
)

参数:

strict_attrs

当在基类上指定时,“严格”属性模式被启用,试图将基类上的 ORM 映射属性限制为仅当下立即存在的属性,同时仍保留“多态”加载行为。

2.0 版中新增。

另请参阅

ConcreteBase

具体表继承

抽象具体类

类签名

sqlalchemy.ext.declarative.AbstractConcreteBase (sqlalchemy.ext.declarative.extensions.ConcreteBase)

代码语言:javascript复制
class sqlalchemy.ext.declarative.ConcreteBase

用于‘具体’声明映射的辅助类。

ConcreteBase 会自动使用 polymorphic_union() 函数,针对所有映射为该类的子类的表。该函数通过 __declare_last__() 函数调用,这实质上是 after_configured() 事件的钩子。

ConcreteBase 为类本身生成一个映射表。与 AbstractConcreteBase 相比,后者不会。

示例:

代码语言:javascript复制
from sqlalchemy.ext.declarative import ConcreteBase

class Employee(ConcreteBase, Base):
    __tablename__ = 'employee'
    employee_id = Column(Integer, primary_key=True)
    name = Column(String(50))
    __mapper_args__ = {
                    'polymorphic_identity':'employee',
                    'concrete':True}

class Manager(Employee):
    __tablename__ = 'manager'
    employee_id = Column(Integer, primary_key=True)
    name = Column(String(50))
    manager_data = Column(String(40))
    __mapper_args__ = {
                    'polymorphic_identity':'manager',
                    'concrete':True}

polymorphic_union() 使用的鉴别器列的默认名称为 type。为了适应映射的用例,其中映射表中的实际列已命名为 type,可以通过设置 _concrete_discriminator_name 属性来配置鉴别器名称:

代码语言:javascript复制
class Employee(ConcreteBase, Base):
    _concrete_discriminator_name = '_concrete_discriminator'

自版本 1.3.19 中新增:为 ConcreteBase 添加了 _concrete_discriminator_name 属性,以便自定义虚拟鉴别器列名称。

自版本 1.4.2 中更改:只需将 _concrete_discriminator_name 属性放置在最基类上即可使所有子类正确生效。如果映射列名称与鉴别器名称冲突,则现在会显示显式错误消息,而在 1.3.x 系列中会有一些警告,然后生成一个无用的查询。

另请参阅

AbstractConcreteBase

具体表继承

代码语言:javascript复制
class sqlalchemy.ext.declarative.DeferredReflection

一个用于基于延迟反射步骤构建映射的辅助类。

通常情况下,通过将一个 Table 对象设置为具有 autoload_with=engine 的 __table__ 属性,可以使用反射来使用声明。一个声明性类。需要注意的是,在构建普通声明性映射的时候,Table 必须是完全反映的,或者至少有一个主键列,这意味着在类声明时必须可用 Engine

DeferredReflection mixin 将映射器的构建移动到稍后的时间点,在调用首先反射到目前为止创建的所有 Table 对象的特定方法之后。类可以定义如下:

代码语言:javascript复制
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.declarative import DeferredReflection
Base = declarative_base()

class MyClass(DeferredReflection, Base):
    __tablename__ = 'mytable'

在上面,MyClass 还没有映射。在上述方式定义了一系列类之后,可以使用 prepare() 反射所有表并创建映射:

代码语言:javascript复制
engine = create_engine("someengine://...")
DeferredReflection.prepare(engine)

DeferredReflection mixin 可以应用于单个类,用作声明基类本身,或用于自定义抽象类。使用抽象基类允许仅为特定准备步骤准备一部分类,这对于使用多个引擎的应用程序是必要的。例如,如果一个应用程序有两个引擎,您可能会使用两个基类,并分别准备每个基类,例如:

代码语言:javascript复制
class ReflectedOne(DeferredReflection, Base):
    __abstract__ = True

class ReflectedTwo(DeferredReflection, Base):
    __abstract__ = True

class MyClass(ReflectedOne):
    __tablename__ = 'mytable'

class MyOtherClass(ReflectedOne):
    __tablename__ = 'myothertable'

class YetAnotherClass(ReflectedTwo):
    __tablename__ = 'yetanothertable'

# ... etc.

在上面,ReflectedOneReflectedTwo 的类层次结构可以分别配置:

代码语言:javascript复制
ReflectedOne.prepare(engine_one)
ReflectedTwo.prepare(engine_two)

成员

prepare()

另请参阅

使用 DeferredReflection - 在 使用声明式配置表 部分。

代码语言:javascript复制
classmethod prepare(bind: Engine | Connection, **reflect_kw: Any) → None

反射所有当前 DeferredReflection 子类的所有 Table 对象

参数:

  • bindEngineConnection 实例 …versionchanged:: 2.0.16 现在也接受 Connection
  • **reflect_kw – 传递给 MetaData.reflect() 的其他关键字参数,例如 MetaData.reflect.views。 新版本 2.0.16 中的内容。

Mypy / Pep-484 对 ORM 映射的支持

原文:docs.sqlalchemy.org/en/20/orm/extensions/mypy.html

当使用直接引用 Column 对象而不是 SQLAlchemy 2.0 中引入的 mapped_column() 构造时,支持 PEP 484 类型注释以及 MyPy 类型检查工具。

自 2.0 版开始已被弃用:SQLAlchemy Mypy 插件已弃用,并且可能在 SQLAlchemy 2.1 发布时被移除。我们建议用户尽快迁移。

无法跨不断变化的 mypy 发布维护此插件,未来的稳定性不能保证。

现代 SQLAlchemy 现在提供了 完全符合 pep-484 的映射语法;请参阅链接的部分以获取迁移详情。

安装

仅适用于 SQLAlchemy 2.0:不应安装存根,并且应完全卸载诸如 sqlalchemy-stubs 和 sqlalchemy2-stubs 等软件包。

Mypy 包本身是一个依赖项。

可以使用 pip 使用“mypy”额外钩子安装 Mypy:

代码语言:javascript复制
pip install sqlalchemy[mypy]

插件本身如 Configuring mypy to use Plugins 中描述的那样配置,使用 sqlalchemy.ext.mypy.plugin 模块名,例如在 setup.cfg 中:

代码语言:javascript复制
[mypy]
plugins = sqlalchemy.ext.mypy.plugin

插件功能

Mypy 插件的主要目的是拦截并修改 SQLAlchemy 声明性映射 的静态定义,使其与它们在被其 Mapper 对象 instrumented 后的结构相匹配。这允许类结构本身以及使用类的代码对 Mypy 工具有意义,否则基于当前声明性映射的功能,这是不可能的。该插件类似于需要为类似 dataclasses 这样的库修改类的动态插件。

为了涵盖这种情况经常发生的主要区域,考虑以下 ORM 映射,使用 User 类的典型示例:

代码语言:javascript复制
from sqlalchemy import Column, Integer, String, select
from sqlalchemy.orm import declarative_base

# "Base" is a class that is created dynamically from the
# declarative_base() function
Base = declarative_base()

class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)

# "some_user" is an instance of the User class, which
# accepts "id" and "name" kwargs based on the mapping
some_user = User(id=5, name="user")

# it has an attribute called .name that's a string
print(f"Username: {some_user.name}")

# a select() construct makes use of SQL expressions derived from the
# User class itself
select_stmt = select(User).where(User.id.in_([3, 4, 5])).where(User.name.contains("s"))

上述,Mypy 扩展可以执行的步骤包括:

  • 解释由 declarative_base() 生成的 Base 动态类,以便从中继承的类被认为是映射的。它还可以适应在使用装饰器进行声明式映射(无声明式基类)中描述的类装饰器方法。
  • 对在声明式“内联”样式中定义的 ORM 映射属性进行类型推断,例如上面示例中 User 类的 idname 属性。这包括 User 的实例将使用 int 类型的 idstr 类型的 name。还包括当访问 User.idUser.name 类级属性时,如上面的 select() 语句中所示,它们与 SQL 表达式行为兼容,这是从 InstrumentedAttribute 属性描述符类派生的。
  • __init__() 方法应用于尚未包含显式构造函数的映射类,该构造函数接受检测到的所有映射属性的特定类型的关键字参数。

当 Mypy 插件处理上述文件时,传递给 Mypy 工具的结果静态类定义和 Python 代码等效于以下内容:

代码语言:javascript复制
from sqlalchemy import Column, Integer, String, select
from sqlalchemy.orm import Mapped
from sqlalchemy.orm.decl_api import DeclarativeMeta

class Base(metaclass=DeclarativeMeta):
    __abstract__ = True

class User(Base):
    __tablename__ = "user"

    id: Mapped[Optional[int]] = Mapped._special_method(
        Column(Integer, primary_key=True)
    )
    name: Mapped[Optional[str]] = Mapped._special_method(Column(String))

    def __init__(self, id: Optional[int] = ..., name: Optional[str] = ...) -> None: ...

some_user = User(id=5, name="user")

print(f"Username: {some_user.name}")

select_stmt = select(User).where(User.id.in_([3, 4, 5])).where(User.name.contains("s"))

上述已经采取的关键步骤包括:

  • Base 类现在明确地是基于 DeclarativeMeta 类定义的,而不再是一个动态类。
  • idname 属性是基于 Mapped 类定义的,该类代表一个在类和实例级别表现出不同行为的 Python 描述符。Mapped 类现在是用于所有 ORM 映射属性的 InstrumentedAttribute 类的基类。 Mapped 被定义为一个针对任意 Python 类型的通用类,这意味着特定的 Mapped 实例与特定的 Python 类型相关联,例如上面的 Mapped[Optional[int]]Mapped[Optional[str]
  • 声明性映射属性赋值的右侧被移除,因为这类似于Mapper类通常要执行的操作,即它将用InstrumentedAttribute](…/internals.html#sqlalchemy.orm.InstrumentedAttribute “sqlalchemy.orm.InstrumentedAttribute”)的特定实例替换这些属性。原始表达式移动到一个函数调用中,这样可以仍然进行类型检查而不与表达式的左侧冲突。对于 Mypy 来说,左侧的类型注释足以理解属性的行为。
  • 添加了User.__init__()方法的类型存根,其中包括了正确的关键字和数据类型。

用法

以下各小节将讨论到目前为止已经考虑到的符合 PEP-484 的各种使用情况。

基于 TypeEngine 的列的内省

对于包含显式数据类型的映射列,当它们被映射为内联属性时,映射类型将被自动内省:

代码语言:javascript复制
class MyClass(Base):
    # ...

    id = Column(Integer, primary_key=True)
    name = Column("employee_name", String(50), nullable=False)
    other_name = Column(String(50))

上述,idnameother_name的最终类级数据类型将被内省为Mapped[Optional[int]]Mapped[Optional[str]]Mapped[Optional[str]]。这些类型默认始终被认为是Optional,即使对于主键和非空列也是如此。原因是因为虽然数据库列idname不能为 NULL,但 Python 属性idname很可能是None,而不需要显式的构造函数:

代码语言:javascript复制
>>> m1 = MyClass()
>>> m1.id
None

上述列的类型可以被显式地声明,提供了更清晰的自我文档化以及能够控制哪些类型是可选的两个优点:

代码语言:javascript复制
class MyClass(Base):
    # ...

    id: int = Column(Integer, primary_key=True)
    name: str = Column("employee_name", String(50), nullable=False)
    other_name: Optional[str] = Column(String(50))

Mypy 插件将接受上述intstrOptional[str]并将它们转换为包含在其周围的Mapped[]类型。Mapped[]构造也可以被显式使用:

代码语言:javascript复制
from sqlalchemy.orm import Mapped

class MyClass(Base):
    # ...

    id: Mapped[int] = Column(Integer, primary_key=True)
    name: Mapped[str] = Column("employee_name", String(50), nullable=False)
    other_name: Mapped[Optional[str]] = Column(String(50))

当类型是非可选时,这意味着从MyClass的实例中访问的属性将被认为是非None的:

代码语言:javascript复制
mc = MyClass(...)

# will pass mypy --strict
name: str = mc.name

对于可选属性,Mypy 认为类型必须包含 None,否则就是Optional

代码语言:javascript复制
mc = MyClass(...)

# will pass mypy --strict
other_name: Optional[str] = mc.name

无论映射的属性是否被标记为Optional__init__()方法的生成都仍然认为所有关键字都是可选的。这再次与 SQLAlchemy ORM 在创建构造函数时实际执行的操作相匹配,不应与诸如 Python dataclasses之类的验证系统的行为混淆,后者将生成一个根据注释匹配的构造函数,包括可选和必需的属性。

没有明确类型的列

包含ForeignKey修饰符的列在 SQLAlchemy 声明映射中不需要指定数据类型。对于这种类型的属性,Mypy 插件将通知用户需要发送明确的类型:

代码语言:javascript复制
# .. other imports
from sqlalchemy.sql.schema import ForeignKey

Base = declarative_base()

class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)

class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    user_id = Column(ForeignKey("user.id"))

插件将按以下方式传递消息:

代码语言:javascript复制
$ mypy test3.py --strict
test3.py:20: error: [SQLAlchemy Mypy plugin] Can't infer type from
ORM mapped expression assigned to attribute 'user_id'; please specify a
Python type or Mapped[<python type>] on the left hand side.
Found 1 error in 1 file (checked 1 source file)

要解决问题,请对Address.user_id列应用明确的类型注释:

代码语言:javascript复制
class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    user_id: int = Column(ForeignKey("user.id"))
使用命令式表映射列

在命令式表样式中,Column定义位于与映射属性本身分开的Table构造内。Mypy 插件不考虑这个Table,而是支持可以明确声明属性,并且必须使用Mapped类将其标识为映射属性:

代码语言:javascript复制
class MyClass(Base):
    __table__ = Table(
        "mytable",
        Base.metadata,
        Column(Integer, primary_key=True),
        Column("employee_name", String(50), nullable=False),
        Column(String(50)),
    )

    id: Mapped[int]
    name: Mapped[str]
    other_name: Mapped[Optional[str]]

上述Mapped注释被视为映射列,并将包含在默认构造函数中,同时为MyClass在类级别和实例级别提供正确的类型配置文件。

映射关系

该插件对使用类型推断来检测关系类型有限支持。对于所有无法检测类型的情况,它将发出信息丰富的错误消息,并且在所有情况下,可以明确提供适当的类型,要么使用Mapped类,要么选择在内联声明中省略它。插件还需要确定关系是指向集合还是标量,并且为此依赖于relationship.uselist和/或relationship.collection_class参数的显式值。如果这些参数都不存在,则需要明确的类型,以及如果relationship()的目标类型是字符串或可调用对象,而不是类:

代码语言:javascript复制
class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)

class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    user_id: int = Column(ForeignKey("user.id"))

    user = relationship(User)

上述映射将产生以下错误:

代码语言:javascript复制
test3.py:22: error: [SQLAlchemy Mypy plugin] Can't infer scalar or
collection for ORM mapped expression assigned to attribute 'user'
if both 'uselist' and 'collection_class' arguments are absent from the
relationship(); please specify a type annotation on the left hand side.
Found 1 error in 1 file (checked 1 source file)

可以通过使用relationship(User, uselist=False)或提供类型来解决错误,在这种情况下是标量User对象:

代码语言:javascript复制
class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    user_id: int = Column(ForeignKey("user.id"))

    user: User = relationship(User)

对于集合,类似的模式也适用,即在没有uselist=Truerelationship.collection_class的情况下,可以使用诸如List之类的集合注释。还可以完全适当地使用类的字符串名称进行注释,如 pep-484 所支持,确保根据需要在TYPE_CHECKING 块中导入类:

代码语言:javascript复制
from typing import TYPE_CHECKING, List

from .mymodel import Base

if TYPE_CHECKING:
    # if the target of the relationship is in another module
    # that cannot normally be imported at runtime
    from .myaddressmodel import Address

class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)
    addresses: List["Address"] = relationship("Address")

与列一样,Mapped 类也可以显式应用:

代码语言:javascript复制
class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)

    addresses: Mapped[List["Address"]] = relationship("Address", back_populates="user")

class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    user_id: int = Column(ForeignKey("user.id"))

    user: Mapped[User] = relationship(User, back_populates="addresses")
使用 @declared_attr 和声明性混合类

declared_attr 类允许在类级别函数中声明声明性映射的属性,并且在使用声明性混合类时特别有用。对于这些函数,函数的返回类型应使用Mapped[]构造或指示函数返回的确切对象类型进行注释。此外,“mixin”类(即不以declarative_base()类扩展,也不使用诸如registry.mapped()之类的方法映射的类)应该被装饰上 declarative_mixin() 装饰器,这为 Mypy 插件提供了一个提示,指明特定的类意图充当声明性混合类:

代码语言:javascript复制
from sqlalchemy.orm import declarative_mixin, declared_attr

@declarative_mixin
class HasUpdatedAt:
    @declared_attr
    def updated_at(cls) -> Column[DateTime]:  # uses Column
        return Column(DateTime)

@declarative_mixin
class HasCompany:
    @declared_attr
    def company_id(cls) -> Mapped[int]:  # uses Mapped
        return Column(ForeignKey("company.id"))

    @declared_attr
    def company(cls) -> Mapped["Company"]:
        return relationship("Company")

class Employee(HasUpdatedAt, HasCompany, Base):
    __tablename__ = "employee"

    id = Column(Integer, primary_key=True)
    name = Column(String)

注意方法HasCompany.company的实际返回类型与注释之间的不匹配。Mypy 插件将所有@declared_attr函数转换为简单的带注释的属性,以避免这种复杂性:

代码语言:javascript复制
# what Mypy sees
class HasCompany:
    company_id: Mapped[int]
    company: Mapped["Company"]
与数据类或其他类型敏感的属性系统相结合

在 将 ORM 映射应用到现有数据类(遗留数据类用法) 中的 Python 数据类集成示例存在一个问题;Python 数据类期望明确的类型,它将用于构建类,并且每个赋值语句中给定的值都是重要的。也就是说,必须确切地声明以下类才能被数据类接受:

代码语言:javascript复制
mapper_registry: registry = registry()

@mapper_registry.mapped
@dataclass
class User:
    __table__ = Table(
        "user",
        mapper_registry.metadata,
        Column("id", Integer, primary_key=True),
        Column("name", String(50)),
        Column("fullname", String(50)),
        Column("nickname", String(12)),
    )
    id: int = field(init=False)
    name: Optional[str] = None
    fullname: Optional[str] = None
    nickname: Optional[str] = None
    addresses: List[Address] = field(default_factory=list)

    __mapper_args__ = {  # type: ignore
        "properties": {"addresses": relationship("Address")}
    }

我们不能将我们的Mapped[]类型应用于属性idname等,因为它们会被@dataclass装饰器拒绝。此外,Mypy 还有另一个专门用于数据类的插件,这也可能妨碍我们的操作。

上述类实际上会顺利通过 Mypy 的类型检查;我们唯一缺少的是在User上的属性可用于 SQL 表达式,例如:

代码语言:javascript复制
stmt = select(User.name).where(User.id.in_([1, 2, 3]))

为了提供一个解决方法,Mypy 插件具有一个额外的功能,我们可以指定一个额外的属性 _mypy_mapped_attrs,它是一个包含类级对象或它们的字符串名称的列表。这个属性可以在 TYPE_CHECKING 变量内部是条件性的:

代码语言:javascript复制
@mapper_registry.mapped
@dataclass
class User:
    __table__ = Table(
        "user",
        mapper_registry.metadata,
        Column("id", Integer, primary_key=True),
        Column("name", String(50)),
        Column("fullname", String(50)),
        Column("nickname", String(12)),
    )
    id: int = field(init=False)
    name: Optional[str] = None
    fullname: Optional[str]
    nickname: Optional[str]
    addresses: List[Address] = field(default_factory=list)

    if TYPE_CHECKING:
        _mypy_mapped_attrs = [id, name, "fullname", "nickname", addresses]

    __mapper_args__ = {  # type: ignore
        "properties": {"addresses": relationship("Address")}
    }

使用上述方法,列在 _mypy_mapped_attrs 中的属性将应用 Mapped 类型信息,以便在类绑定上下文中使用 User 类时,它将表现为一个 SQLAlchemy 映射类。

安装

对于 仅适用于 SQLAlchemy 2.0:不应安装存根,而应完全卸载像 sqlalchemy-stubs 和 sqlalchemy2-stubs 这样的包。

Mypy 包本身是一个依赖项。

可以使用 pip 使用 “mypy” extras 钩子安装 Mypy:

代码语言:javascript复制
pip install sqlalchemy[mypy]

插件本身配置如 配置 mypy 使用插件 中所述,使用 sqlalchemy.ext.mypy.plugin 模块名称,例如在 setup.cfg 中:

代码语言:javascript复制
[mypy]
plugins = sqlalchemy.ext.mypy.plugin

插件的功能

Mypy 插件的主要目的是拦截和修改 SQLAlchemy 声明式映射 的静态定义,以使其与它们在被 Mapper 对象 仪器化 后的结构相匹配。这使得类结构本身以及使用类的代码对 Mypy 工具有意义,否则根据当前声明式映射的功能,这将不是情况。该插件类似于为像 dataclasses 这样的库所需的类似插件,这些插件在运行时动态地修改类。

要涵盖此类情况经常发生的主要领域,请考虑以下 ORM 映射,使用 User 类的典型示例:

代码语言:javascript复制
from sqlalchemy import Column, Integer, String, select
from sqlalchemy.orm import declarative_base

# "Base" is a class that is created dynamically from the
# declarative_base() function
Base = declarative_base()

class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)

# "some_user" is an instance of the User class, which
# accepts "id" and "name" kwargs based on the mapping
some_user = User(id=5, name="user")

# it has an attribute called .name that's a string
print(f"Username: {some_user.name}")

# a select() construct makes use of SQL expressions derived from the
# User class itself
select_stmt = select(User).where(User.id.in_([3, 4, 5])).where(User.name.contains("s"))

上面,Mypy 扩展可以执行的步骤包括:

  • 对由 declarative_base() 生成的 Base 动态类进行解释,以便继承它的类被知道是已映射的。它还可以适应 使用装饰器进行声明式映射(无声明式基类) 中描述的类装饰器方法。
  • 对于在声明式“内联”样式中定义的 ORM 映射属性的类型推断,例如上面示例中 User 类的 idname 属性。这包括 User 实例将使用 int 类型的 idstr 类型的 name。它还包括当访问 User.idUser.name 类级属性时,正如它们在上面的 select() 语句中那样,它们与 SQL 表达式行为兼容,这是从 InstrumentedAttribute 属性描述符类派生的。
  • __init__() 方法应用于尚未包含显式构造函数的映射类,该构造函数接受特定类型的关键字参数,用于检测到的所有映射属性。

当 Mypy 插件处理上述文件时,结果的静态类定义和传递给 Mypy 工具的 Python 代码等效于以下内容:

代码语言:javascript复制
from sqlalchemy import Column, Integer, String, select
from sqlalchemy.orm import Mapped
from sqlalchemy.orm.decl_api import DeclarativeMeta

class Base(metaclass=DeclarativeMeta):
    __abstract__ = True

class User(Base):
    __tablename__ = "user"

    id: Mapped[Optional[int]] = Mapped._special_method(
        Column(Integer, primary_key=True)
    )
    name: Mapped[Optional[str]] = Mapped._special_method(Column(String))

    def __init__(self, id: Optional[int] = ..., name: Optional[str] = ...) -> None: ...

some_user = User(id=5, name="user")

print(f"Username: {some_user.name}")

select_stmt = select(User).where(User.id.in_([3, 4, 5])).where(User.name.contains("s"))

以上已经采取的关键步骤包括:

  • Base 类现在明确地以 DeclarativeMeta 类的形式定义,而不是动态类。
  • idname 属性是以 Mapped 类的术语定义的,该类表示在类与实例级别上表现出不同行为的 Python 描述符。Mapped 类现在是用于所有 ORM 映射属性的 InstrumentedAttribute 类的基类。 Mapped 被定义为针对任意 Python 类型的通用类,这意味着 Mapped 的特定出现与特定的 Python 类型相关联,例如上面的 Mapped[Optional[int]]Mapped[Optional[str]]
  • 声明式映射属性分配的右侧 已移除,因为这类似于 Mapper 类通常会执行的操作,即它将这些属性替换为 InstrumentedAttribute 的具体实例。原始表达式移到一个函数调用中,这将允许它仍然被类型检查而不与表达式的左侧发生冲突。对于 Mypy 来说,左侧的类型注释足以理解属性的行为。
  • User.__init__() 方法添加了类型存根,其中包括正确的关键字和数据类型。

使用方法

以下各小节将讨论迄今为止已考虑到的个别用例的 pep-484 符合性。

基于 TypeEngine 的列的自省

对于包含显式数据类型的映射列,当它们作为内联属性映射时,映射类型将被自动解析:

代码语言:javascript复制
class MyClass(Base):
    # ...

    id = Column(Integer, primary_key=True)
    name = Column("employee_name", String(50), nullable=False)
    other_name = Column(String(50))

在上面,idnameother_name 这些最终的类级别数据类型将被解析为 Mapped[Optional[int]]Mapped[Optional[str]]Mapped[Optional[str]]。这些类型默认情况下总是被认为是 Optional 的,即使对于主键和非空列也是如此。原因是因为虽然数据库列idname不能为 NULL,但 Python 属性 idname 在没有显式构造函数的情况下肯定可以是 None

代码语言:javascript复制
>>> m1 = MyClass()
>>> m1.id
None

上述列的类型可以被显式地声明,提供了更清晰的自我文档说明以及能够控制哪些类型是可选的两个优点:

代码语言:javascript复制
class MyClass(Base):
    # ...

    id: int = Column(Integer, primary_key=True)
    name: str = Column("employee_name", String(50), nullable=False)
    other_name: Optional[str] = Column(String(50))

Mypy 插件将接受上述的 intstrOptional[str],并将它们转换为包含在 Mapped[] 类型周围的类型。Mapped[] 结构也可以被显式使用:

代码语言:javascript复制
from sqlalchemy.orm import Mapped

class MyClass(Base):
    # ...

    id: Mapped[int] = Column(Integer, primary_key=True)
    name: Mapped[str] = Column("employee_name", String(50), nullable=False)
    other_name: Mapped[Optional[str]] = Column(String(50))

当类型是非可选时,这意味着从 MyClass 实例访问的属性将被视为非 None:

代码语言:javascript复制
mc = MyClass(...)

# will pass mypy --strict
name: str = mc.name

对于可选属性,Mypy 认为类型必须包括 None,否则为 Optional

代码语言:javascript复制
mc = MyClass(...)

# will pass mypy --strict
other_name: Optional[str] = mc.name

无论映射属性是否被标记为 Optional,生成的 __init__() 方法仍然将所有关键字视为可选的。这再次与 SQLAlchemy ORM 实际创建构造函数时的行为相匹配,不应与诸如 Python dataclasses 之类的验证系统的行为混淆,后者将生成一个与注释匹配的构造函数,以确定可选 vs. 必需属性的注解。

没有明确类型的列

包含 ForeignKey 修改器的列在 SQLAlchemy 声明式映射中不需要指定数据类型。对于这种类型的属性,Mypy 插件将通知用户需要发送一个显式类型:

代码语言:javascript复制
# .. other imports
from sqlalchemy.sql.schema import ForeignKey

Base = declarative_base()

class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)

class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    user_id = Column(ForeignKey("user.id"))

插件将如下传递消息:

代码语言:javascript复制
$ mypy test3.py --strict
test3.py:20: error: [SQLAlchemy Mypy plugin] Can't infer type from
ORM mapped expression assigned to attribute 'user_id'; please specify a
Python type or Mapped[<python type>] on the left hand side.
Found 1 error in 1 file (checked 1 source file)

要解决此问题,请为 Address.user_id 列应用显式类型注释:

代码语言:javascript复制
class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    user_id: int = Column(ForeignKey("user.id"))
使用命令式表格映射列

在命令式表格风格中,Column 定义位于一个与映射属性本身分离的 Table 结构内。Mypy 插件不考虑这个 Table,而是支持可以显式声明属性,必须使用 Mapped 类来标识它们为映射属性:

代码语言:javascript复制
class MyClass(Base):
    __table__ = Table(
        "mytable",
        Base.metadata,
        Column(Integer, primary_key=True),
        Column("employee_name", String(50), nullable=False),
        Column(String(50)),
    )

    id: Mapped[int]
    name: Mapped[str]
    other_name: Mapped[Optional[str]]

上述Mapped注释被视为映射列,并将包含在默认构造函数中,以及在类级别和实例级别为MyClass提供正确的类型配置文件。

映射关系

该插件对使用类型推断来检测关系的类型有限支持。对于所有这些无法检测到类型的情况,它都将发出一个信息丰富的错误消息,在所有情况下,可以明确提供适当的类型,要么使用Mapped类,要么选择在内联声明中省略它。该插件还需要确定关系是指向集合还是标量,并且为此依赖于relationship.uselist和/或relationship.collection_class参数的显式值。如果这些参数都不存在,则需要明确的类型,以及如果relationship()的目标类型是字符串或可调用对象而不是类,则也需要明确的类型:

代码语言:javascript复制
class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)

class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    user_id: int = Column(ForeignKey("user.id"))

    user = relationship(User)

上述映射将产生以下错误:

代码语言:javascript复制
test3.py:22: error: [SQLAlchemy Mypy plugin] Can't infer scalar or
collection for ORM mapped expression assigned to attribute 'user'
if both 'uselist' and 'collection_class' arguments are absent from the
relationship(); please specify a type annotation on the left hand side.
Found 1 error in 1 file (checked 1 source file)

错误可以通过使用relationship(User, uselist=False)或者提供类型来解决,例如标量User对象:

代码语言:javascript复制
class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    user_id: int = Column(ForeignKey("user.id"))

    user: User = relationship(User)

对于集合,类似的模式也适用,如果没有uselist=True或者relationship.collection_class,可以使用集合注释,如List。在注释中使用类的字符串名称也是完全合适的,这是由 pep-484 支持的,确保在适当的时候在TYPE_CHECKING 块中导入该类:

代码语言:javascript复制
from typing import TYPE_CHECKING, List

from .mymodel import Base

if TYPE_CHECKING:
    # if the target of the relationship is in another module
    # that cannot normally be imported at runtime
    from .myaddressmodel import Address

class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)
    addresses: List["Address"] = relationship("Address")

与列相似,Mapped类也可以显式地应用:

代码语言:javascript复制
class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)

    addresses: Mapped[List["Address"]] = relationship("Address", back_populates="user")

class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    user_id: int = Column(ForeignKey("user.id"))

    user: Mapped[User] = relationship(User, back_populates="addresses")
使用 @declared_attr 和声明性混合

declared_attr类允许在类级函数中声明 Declarative 映射属性,并且在使用声明性混入时特别有用。对于这些函数,函数的返回类型应该使用Mapped[]构造进行注释,或者指示函数返回的对象的确切类型。此外,未被映射的“混入”类(即不从declarative_base()类继承,也不使用诸如registry.mapped()之类的方法进行映射)应该用declarative_mixin()装饰器进行修饰,这为 Mypy 插件提供了一个提示,表明特定类意图作为声明性混入:

代码语言:javascript复制
from sqlalchemy.orm import declarative_mixin, declared_attr

@declarative_mixin
class HasUpdatedAt:
    @declared_attr
    def updated_at(cls) -> Column[DateTime]:  # uses Column
        return Column(DateTime)

@declarative_mixin
class HasCompany:
    @declared_attr
    def company_id(cls) -> Mapped[int]:  # uses Mapped
        return Column(ForeignKey("company.id"))

    @declared_attr
    def company(cls) -> Mapped["Company"]:
        return relationship("Company")

class Employee(HasUpdatedAt, HasCompany, Base):
    __tablename__ = "employee"

    id = Column(Integer, primary_key=True)
    name = Column(String)

注意像HasCompany.company这样的方法的实际返回类型与注释的不匹配。Mypy 插件将所有@declared_attr函数转换为简单的注释属性,以避免这种复杂性:

代码语言:javascript复制
# what Mypy sees
class HasCompany:
    company_id: Mapped[int]
    company: Mapped["Company"]
与 Dataclasses 或其他类型敏感的属性系统结合

Python dataclasses 集成的示例在将 ORM 映射应用于现有数据类(传统数据类用法)中提出了一个问题;Python dataclasses 期望一个明确的类型,它将用于构建类,并且每个赋值语句中给定的值是重要的。也就是说,一个如下所示的类必须要准确地声明才能被 dataclasses 接受:

代码语言:javascript复制
mapper_registry: registry = registry()

@mapper_registry.mapped
@dataclass
class User:
    __table__ = Table(
        "user",
        mapper_registry.metadata,
        Column("id", Integer, primary_key=True),
        Column("name", String(50)),
        Column("fullname", String(50)),
        Column("nickname", String(12)),
    )
    id: int = field(init=False)
    name: Optional[str] = None
    fullname: Optional[str] = None
    nickname: Optional[str] = None
    addresses: List[Address] = field(default_factory=list)

    __mapper_args__ = {  # type: ignore
        "properties": {"addresses": relationship("Address")}
    }

我们无法将我们的Mapped[]类型应用于属性idname等,因为它们将被@dataclass装饰器拒绝。此外,Mypy 还有另一个专门用于 dataclasses 的插件,这也可能影响我们的操作。

上述类实际上会通过 Mypy 的类型检查而没有问题;我们唯一缺少的是User上的属性能够在 SQL 表达式中使用,比如:

代码语言:javascript复制
stmt = select(User.name).where(User.id.in_([1, 2, 3]))

为了解决这个问题,Mypy 插件有一个额外的功能,我们可以指定一个额外的属性_mypy_mapped_attrs,这是一个包含类级对象或它们的字符串名称的列表。这个属性可以在TYPE_CHECKING变量内部进行条件判断:

代码语言:javascript复制
@mapper_registry.mapped
@dataclass
class User:
    __table__ = Table(
        "user",
        mapper_registry.metadata,
        Column("id", Integer, primary_key=True),
        Column("name", String(50)),
        Column("fullname", String(50)),
        Column("nickname", String(12)),
    )
    id: int = field(init=False)
    name: Optional[str] = None
    fullname: Optional[str]
    nickname: Optional[str]
    addresses: List[Address] = field(default_factory=list)

    if TYPE_CHECKING:
        _mypy_mapped_attrs = [id, name, "fullname", "nickname", addresses]

    __mapper_args__ = {  # type: ignore
        "properties": {"addresses": relationship("Address")}
    }

使用上述方法,列在_mypy_mapped_attrs中列出的属性将应用于Mapped类型信息,以便在类绑定上下文中使用User类时,它将表现为一个 SQLAlchemy 映射类。

基于 TypeEngine 的列的内省

对于包含显式数据类型的映射列,当它们被映射为内联属性时,映射类型将自动进行内省:

代码语言:javascript复制
class MyClass(Base):
    # ...

    id = Column(Integer, primary_key=True)
    name = Column("employee_name", String(50), nullable=False)
    other_name = Column(String(50))

在上面,idnameother_name的最终类级数据类型将被内省为Mapped[Optional[int]]Mapped[Optional[str]]Mapped[Optional[str]]。类型默认始终被视为可选,即使对于主键和非空列也是如此。原因是因为虽然数据库列idname不能为 NULL,但 Python 属性idname可以毫无疑问地是None,而不需要显式构造函数:

代码语言:javascript复制
>>> m1 = MyClass()
>>> m1.id
None

上述列的类型可以明确声明,提供两个优势,即更清晰的自我文档化以及能够控制哪些类型是可选的:

代码语言:javascript复制
class MyClass(Base):
    # ...

    id: int = Column(Integer, primary_key=True)
    name: str = Column("employee_name", String(50), nullable=False)
    other_name: Optional[str] = Column(String(50))

Mypy 插件将接受上述intstrOptional[str],并将它们转换为包围它们的Mapped[]类型。Mapped[]结构也可以明确使用:

代码语言:javascript复制
from sqlalchemy.orm import Mapped

class MyClass(Base):
    # ...

    id: Mapped[int] = Column(Integer, primary_key=True)
    name: Mapped[str] = Column("employee_name", String(50), nullable=False)
    other_name: Mapped[Optional[str]] = Column(String(50))

当类型为非可选时,这意味着从MyClass实例中访问的属性将被视为非None

代码语言:javascript复制
mc = MyClass(...)

# will pass mypy --strict
name: str = mc.name

对于可选属性,Mypy 认为类型必须包含 None,否则为Optional

代码语言:javascript复制
mc = MyClass(...)

# will pass mypy --strict
other_name: Optional[str] = mc.name

无论映射属性是否被标记为Optional__init__()方法的生成仍然考虑所有关键字都是可选的。这再次与 SQLAlchemy ORM 实际创建构造函数时的行为相匹配,不应与验证系统(如 Python dataclasses)的行为混淆,后者将根据注释生成与可选与必需属性相匹配的构造函数。

不具有显式类型的列

包含 ForeignKey 修改器的列在 SQLAlchemy 声明性映射中不需要指定数据类型。对于这种类型的属性,Mypy 插件将通知用户需要发送显式类型:

代码语言:javascript复制
# .. other imports
from sqlalchemy.sql.schema import ForeignKey

Base = declarative_base()

class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)

class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    user_id = Column(ForeignKey("user.id"))

插件将以以下方式发送消息:

代码语言:javascript复制
$ mypy test3.py --strict
test3.py:20: error: [SQLAlchemy Mypy plugin] Can't infer type from
ORM mapped expression assigned to attribute 'user_id'; please specify a
Python type or Mapped[<python type>] on the left hand side.
Found 1 error in 1 file (checked 1 source file)

要解决此问题,请对Address.user_id列应用显式类型注释:

代码语言:javascript复制
class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    user_id: int = Column(ForeignKey("user.id"))
使用命令式表映射列

在 命令式表风格 中,Column 定义放在一个独立于映射属性本身的 Table 结构中。Mypy 插件不考虑这个Table,而是支持可以明确声明属性,并且必须使用 Mapped 类来标识它们为映射属性:

代码语言:javascript复制
class MyClass(Base):
    __table__ = Table(
        "mytable",
        Base.metadata,
        Column(Integer, primary_key=True),
        Column("employee_name", String(50), nullable=False),
        Column(String(50)),
    )

    id: Mapped[int]
    name: Mapped[str]
    other_name: Mapped[Optional[str]]

上述Mapped注释被视为映射列,并将包含在默认构造函数中,同时为MyClass提供正确的类型配置文件,无论是在类级别还是实例级别。

映射关系

该插件对使用类型推断来检测关系类型有限支持。对于所有无法检测类型的情况,它将发出信息丰富的错误消息,并且在所有情况下,可以明确提供适当的类型,可以使用Mapped类或选择性地省略内联声明。插件还需要确定关系是引用集合还是标量,为此依赖于relationship.uselist和/或relationship.collection_class参数的显式值。如果这些参数都不存在,则需要明确类型,以及如果relationship()的目标类型是字符串或可调用的,而不是类:

代码语言:javascript复制
class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)

class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    user_id: int = Column(ForeignKey("user.id"))

    user = relationship(User)

上述映射将产生以下错误:

代码语言:javascript复制
test3.py:22: error: [SQLAlchemy Mypy plugin] Can't infer scalar or
collection for ORM mapped expression assigned to attribute 'user'
if both 'uselist' and 'collection_class' arguments are absent from the
relationship(); please specify a type annotation on the left hand side.
Found 1 error in 1 file (checked 1 source file)

可以通过使用relationship(User, uselist=False)或提供类型来解决错误,在这种情况下是标量User对象:

代码语言:javascript复制
class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    user_id: int = Column(ForeignKey("user.id"))

    user: User = relationship(User)

对于集合,类似的模式适用,如果没有uselist=Truerelationship.collection_class,可以使用List等集合注释。在注释中使用类的字符串名称也是完全适当的,支持 pep-484,确保类在TYPE_CHECKING block中适当导入:

代码语言:javascript复制
from typing import TYPE_CHECKING, List

from .mymodel import Base

if TYPE_CHECKING:
    # if the target of the relationship is in another module
    # that cannot normally be imported at runtime
    from .myaddressmodel import Address

class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)
    addresses: List["Address"] = relationship("Address")

与列一样,Mapped类也可以显式应用:

代码语言:javascript复制
class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)

    addresses: Mapped[List["Address"]] = relationship("Address", back_populates="user")

class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    user_id: int = Column(ForeignKey("user.id"))

    user: Mapped[User] = relationship(User, back_populates="addresses")
使用@declared_attr 和声明性混合

declared_attr 类允许在类级函数中声明声明性映射属性,并且在使用声明性混合时特别有用。对于这些函数,函数的返回类型应该使用Mapped[]构造进行注释,或者指示函数返回的确切对象类型。另外,未以其他方式映射的“混合”类(即不从declarative_base()类扩展,也不使用诸如registry.mapped()之类的方法进行映射)应该用declarative_mixin()装饰器进行装饰,这为 Mypy 插件提供了一个提示,表明特定的类打算作为声明性混合使用:

代码语言:javascript复制
from sqlalchemy.orm import declarative_mixin, declared_attr

@declarative_mixin
class HasUpdatedAt:
    @declared_attr
    def updated_at(cls) -> Column[DateTime]:  # uses Column
        return Column(DateTime)

@declarative_mixin
class HasCompany:
    @declared_attr
    def company_id(cls) -> Mapped[int]:  # uses Mapped
        return Column(ForeignKey("company.id"))

    @declared_attr
    def company(cls) -> Mapped["Company"]:
        return relationship("Company")

class Employee(HasUpdatedAt, HasCompany, Base):
    __tablename__ = "employee"

    id = Column(Integer, primary_key=True)
    name = Column(String)

请注意,像HasCompany.company这样的方法的实际返回类型与其注释之间存在不匹配。Mypy 插件将所有@declared_attr函数转换为简单的注释属性,以避免这种复杂性:

代码语言:javascript复制
# what Mypy sees
class HasCompany:
    company_id: Mapped[int]
    company: Mapped["Company"]
与数据类或其他类型敏感的属性系统结合

Python 数据类集成示例中的将 ORM 映射应用到现有数据类(旧数据类使用)存在一个问题;Python 数据类期望一个明确的类型,它将用于构建类,并且在每个赋值语句中给定的值是重要的。也就是说,必须准确地声明如下的类才能被数据类接受:

代码语言:javascript复制
mapper_registry: registry = registry()

@mapper_registry.mapped
@dataclass
class User:
    __table__ = Table(
        "user",
        mapper_registry.metadata,
        Column("id", Integer, primary_key=True),
        Column("name", String(50)),
        Column("fullname", String(50)),
        Column("nickname", String(12)),
    )
    id: int = field(init=False)
    name: Optional[str] = None
    fullname: Optional[str] = None
    nickname: Optional[str] = None
    addresses: List[Address] = field(default_factory=list)

    __mapper_args__ = {  # type: ignore
        "properties": {"addresses": relationship("Address")}
    }

我们无法将我们的Mapped[]类型应用于属性idname等,因为它们将被@dataclass装饰器拒绝。另外,Mypy 还有另一个专门针对数据类的插件,这也可能妨碍我们的操作。

上述类实际上将无障碍地通过 Mypy 的类型检查;我们唯一缺少的是User上属性被用于 SQL 表达式的能力,例如:

代码语言:javascript复制
stmt = select(User.name).where(User.id.in_([1, 2, 3]))

为此提供一种解决方案,Mypy 插件具有一个额外的功能,我们可以指定一个额外的属性_mypy_mapped_attrs,它是一个包含类级对象或它们的字符串名称的列表。该属性可以在TYPE_CHECKING变量中条件化:

代码语言:javascript复制
@mapper_registry.mapped
@dataclass
class User:
    __table__ = Table(
        "user",
        mapper_registry.metadata,
        Column("id", Integer, primary_key=True),
        Column("name", String(50)),
        Column("fullname", String(50)),
        Column("nickname", String(12)),
    )
    id: int = field(init=False)
    name: Optional[str] = None
    fullname: Optional[str]
    nickname: Optional[str]
    addresses: List[Address] = field(default_factory=list)

    if TYPE_CHECKING:
        _mypy_mapped_attrs = [id, name, "fullname", "nickname", addresses]

    __mapper_args__ = {  # type: ignore
        "properties": {"addresses": relationship("Address")}
    }

使用上述方法,将在_mypy_mapped_attrs中列出的属性应用Mapped类型信息,以便在类绑定上下文中使用User类时,它将表现为 SQLAlchemy 映射类。

突变跟踪

原文:docs.sqlalchemy.org/en/20/orm/extensions/mutable.html

提供对标量值的就地更改的跟踪支持,这些更改传播到拥有父对象上的 ORM 更改事件中。

在标量列值上建立可变性

“可变”结构的典型示例是 Python 字典。按照 SQL 数据类型对象 中介绍的示例,我们从一个自定义类型开始,该类型将 Python 字典编组为 JSON 字符串,然后再进行持久化:

代码语言:javascript复制
from sqlalchemy.types import TypeDecorator, VARCHAR
import json

class JSONEncodedDict(TypeDecorator):
    "Represents an immutable structure as a json-encoded string."

    impl = VARCHAR

    def process_bind_param(self, value, dialect):
        if value is not None:
            value = json.dumps(value)
        return value

    def process_result_value(self, value, dialect):
        if value is not None:
            value = json.loads(value)
        return value

仅出于示例目的使用 jsonsqlalchemy.ext.mutable 扩展可与任何目标 Python 类型可能是可变的类型一起使用,包括 PickleTypeARRAY 等。

使用 sqlalchemy.ext.mutable 扩展时,值本身会跟踪所有引用它的父对象。下面,我们展示了 MutableDict 字典对象的简单版本,它将 Mutable mixin 应用于普通 Python 字典:

代码语言:javascript复制
from sqlalchemy.ext.mutable import Mutable

class MutableDict(Mutable, dict):
    @classmethod
    def coerce(cls, key, value):
        "Convert plain dictionaries to MutableDict."

        if not isinstance(value, MutableDict):
            if isinstance(value, dict):
                return MutableDict(value)

            # this call will raise ValueError
            return Mutable.coerce(key, value)
        else:
            return value

    def __setitem__(self, key, value):
        "Detect dictionary set events and emit change events."

        dict.__setitem__(self, key, value)
        self.changed()

    def __delitem__(self, key):
        "Detect dictionary del events and emit change events."

        dict.__delitem__(self, key)
        self.changed()

上述字典类采用了子类化 Python 内置的 dict 的方法,以生成一个 dict 子类,该子类通过 __setitem__ 将所有突变事件路由到。这种方法有其变体,例如子类化 UserDict.UserDictcollections.MutableMapping;对于此示例而言,重要的部分是当数据结构发生就地更改时,将调用 Mutable.changed() 方法。

我们还重新定义了 Mutable.coerce() 方法,该方法将用于将不是 MutableDict 实例的任何值转换为适当的类型,例如 json 模块返回的普通字典。定义此方法是可选的;我们也可以创建我们的 JSONEncodedDict,使其始终返回 MutableDict 的实例,并且还确保所有调用代码都显式使用 MutableDict。当未覆盖 Mutable.coerce() 时,应用于父对象的任何不是可变类型实例的值都将引发 ValueError

我们的新 MutableDict 类型提供了一个类方法 Mutable.as_mutable(),我们可以在列元数据中使用它来关联类型。该方法获取给定的类型对象或类,并关联一个监听器,该监听器将检测到该类型的所有未来映射,并对映射的属性应用事件监听仪器。例如,使用经典的表元数据:

代码语言:javascript复制
from sqlalchemy import Table, Column, Integer

my_data = Table('my_data', metadata,
    Column('id', Integer, primary_key=True),
    Column('data', MutableDict.as_mutable(JSONEncodedDict))
)

在上面,Mutable.as_mutable() 返回一个 JSONEncodedDict 实例(如果类型对象尚不是实例),该实例将拦截针对该类型映射的任何属性。下面我们建立一个简单的映射与 my_data 表:

代码语言:javascript复制
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column

class Base(DeclarativeBase):
    pass

class MyDataClass(Base):
    __tablename__ = 'my_data'
    id: Mapped[int] = mapped_column(primary_key=True)
    data: Mapped[dict[str, str]] = mapped_column(MutableDict.as_mutable(JSONEncodedDict))

MyDataClass.data 成员现在将收到对其值的原地更改的通知。

MyDataClass.data 成员的任何原地更改都会在父对象上标记属性为“脏”:

代码语言:javascript复制
>>> from sqlalchemy.orm import Session

>>> sess = Session(some_engine)
>>> m1 = MyDataClass(data={'value1':'foo'})
>>> sess.add(m1)
>>> sess.commit()

>>> m1.data['value1'] = 'bar'
>>> assert m1 in sess.dirty
True

MutableDict 可以通过一步关联所有未来的 JSONEncodedDict 实例,使用 Mutable.associate_with()。这类似于 Mutable.as_mutable(),但它将无条件地拦截所有映射中所有 MutableDict 的出现,而无需单独声明它:

代码语言:javascript复制
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column

MutableDict.associate_with(JSONEncodedDict)

class Base(DeclarativeBase):
    pass

class MyDataClass(Base):
    __tablename__ = 'my_data'
    id: Mapped[int] = mapped_column(primary_key=True)
    data: Mapped[dict[str, str]] = mapped_column(JSONEncodedDict)
支持 Pickling

sqlalchemy.ext.mutable 扩展的关键在于在值对象上放置了一个 weakref.WeakKeyDictionary,它存储了父映射对象到与该值相关联的属性名称的映射。 WeakKeyDictionary 对象不可 pickle,因为它们包含 weakrefs 和函数回调。在我们的情况下,这是件好事,因为如果这个字典是可 pickle 的,那么它可能会导致我们的值对象的 pickle 大小过大,因为它们在不涉及父对象上下文的情况下被单独 pickle。开发人员在这里的责任只是提供一个 __getstate__ 方法,该方法将 MutableBase._parents() 集合从 pickle 流中排除:

代码语言:javascript复制
class MyMutableType(Mutable):
    def __getstate__(self):
        d = self.__dict__.copy()
        d.pop('_parents', None)
        return d

对于我们的字典示例,我们需要返回字典本身的内容(并在 __setstate__ 上也进行恢复):

代码语言:javascript复制
class MutableDict(Mutable, dict):
    # ....

    def __getstate__(self):
        return dict(self)

    def __setstate__(self, state):
        self.update(state)

如果我们的可变值对象作为它附加到的一个或多个父对象一起被 pickle,那么 Mutable mixin 将在每个值对象上重新建立 Mutable._parents 集合,因为拥有父对象本身被 unpickle。

接收事件

AttributeEvents.modified() 事件处理程序可用于在可变标量发出更改事件时接收事件。 当从可变扩展内调用 flag_modified() 函数时,将调用此事件处理程序:

代码语言:javascript复制
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy import event

class Base(DeclarativeBase):
    pass

class MyDataClass(Base):
    __tablename__ = 'my_data'
    id: Mapped[int] = mapped_column(primary_key=True)
    data: Mapped[dict[str, str]] = mapped_column(MutableDict.as_mutable(JSONEncodedDict))

@event.listens_for(MyDataClass.data, "modified")
def modified_json(instance, initiator):
    print("json value modified:", instance.data)
```## 在复合上建立可变性

复合是一种特殊的 ORM 功能,允许将单个标量属性分配给一个对象值,该对象值表示从底层映射表的一个或多个列中“组合”而成的信息。 通常示例是几何“点”,并在 复合列类型 中介绍。

与 `Mutable` 一样,用户定义的复合类将 `MutableComposite` 作为一个混合类,通过 `MutableComposite.changed()` 方法检测并传递更改事件给其父对象。 在复合类的情况下,检测通常通过特殊的 Python 方法 `__setattr__()` 进行。 在下面的示例中,我们扩展了 复合列类型 中介绍的 `Point` 类,以包括 `MutableComposite` 在其基类中,并通过 `__setattr__` 将属性设置事件路由到 `MutableComposite.changed()` 方法:

```py
import dataclasses
from sqlalchemy.ext.mutable import MutableComposite

@dataclasses.dataclass
class Point(MutableComposite):
    x: int
    y: int

    def __setattr__(self, key, value):
        "Intercept set events"

        # set the attribute
        object.__setattr__(self, key, value)

        # alert all parents to the change
        self.changed()

MutableComposite 类利用类映射事件自动为任何使用指定我们的 Point 类型的 composite() 的地方建立监听器。 下面,当 Point 映射到 Vertex 类时,将建立监听器,这些监听器将将来自 Point 对象的更改事件路由到每个 Vertex.startVertex.end 属性:

代码语言:javascript复制
from sqlalchemy.orm import DeclarativeBase, Mapped
from sqlalchemy.orm import composite, mapped_column

class Base(DeclarativeBase):
    pass

class Vertex(Base):
    __tablename__ = "vertices"

    id: Mapped[int] = mapped_column(primary_key=True)

    start: Mapped[Point] = composite(mapped_column("x1"), mapped_column("y1"))
    end: Mapped[Point] = composite(mapped_column("x2"), mapped_column("y2"))

    def __repr__(self):
        return f"Vertex(start={self.start}, end={self.end})"

Vertex.startVertex.end 成员的任何原地更改都将在父对象上标记该属性为“脏”:

代码语言:javascript复制
>>> from sqlalchemy.orm import Session
>>> sess = Session(engine)
>>> v1 = Vertex(start=Point(3, 4), end=Point(12, 15))
>>> sess.add(v1)
sql>>> sess.flush()
BEGIN  (implicit)
INSERT  INTO  vertices  (x1,  y1,  x2,  y2)  VALUES  (?,  ?,  ?,  ?)
[...]  (3,  4,  12,  15)
>>> v1.end.x = 8
>>> assert v1 in sess.dirty
True
sql>>> sess.commit()
UPDATE  vertices  SET  x2=?  WHERE  vertices.id  =  ?
[...]  (8,  1)
COMMIT 
强制转换可变组合

MutableBase.coerce() 方法也支持复合类型。对于 MutableCompositeMutableBase.coerce() 方法仅在属性设置操作时调用,而不在加载操作中调用。覆盖 MutableBase.coerce() 方法基本上等同于为使用自定义复合类型的所有属性使用 validates() 验证程序:

代码语言:javascript复制
@dataclasses.dataclass
class Point(MutableComposite):
    # other Point methods
    # ...

    def coerce(cls, key, value):
        if isinstance(value, tuple):
            value = Point(*value)
        elif not isinstance(value, Point):
            raise ValueError("tuple or Point expected")
        return value
支持 Pickling

Mutable 类似,MutableComposite 辅助类使用 weakref.WeakKeyDictionary,可通过 MutableBase._parents() 属性获得,该属性不可 picklable。如果我们需要 pickle Point 的实例或其所属的类 Vertex,我们至少需要定义一个不包含 _parents 字典的 __getstate__。下面我们定义了 Point 类的最小形式的 __getstate____setstate__

代码语言:javascript复制
@dataclasses.dataclass
class Point(MutableComposite):
    # ...

    def __getstate__(self):
        return self.x, self.y

    def __setstate__(self, state):
        self.x, self.y = state

Mutable 一样,MutableComposite 增强了父对象的对象关系状态的 pickling 过程,以便 MutableBase._parents() 集合被恢复到所有 Point 对象中。

API 参考

对象名称

描述

Mutable

混合类,定义对父对象的变更事件的透明传播。

MutableBase

Mutable 和 MutableComposite 的通用基类。

MutableComposite

混合类,定义对 SQLAlchemy “composite” 对象的变更事件的透明传播,传播到其拥有的父对象或父对象。

MutableDict

实现了 Mutable 的字典类型。

MutableList

实现了 Mutable 的列表类型。

MutableSet

实现Mutable的集合类型。

代码语言:javascript复制
class sqlalchemy.ext.mutable.MutableBase

成员

_parents,coerce()

公共基类,用于MutableMutableComposite

代码语言:javascript复制
attribute _parents

父对象的InstanceState->父对象上的属性名称的字典。

该属性是所谓的“记忆化”属性。首次访问时,它会使用一个新的weakref.WeakKeyDictionary进行初始化,并在后续访问时返回相同的对象。

在 1.4 版本中更改:现在使用InstanceState作为弱字典中的键,而不是实例本身。

代码语言:javascript复制
classmethod coerce(key: str, value: Any) → Any | None

给定一个值,将其强制转换为目标类型。

可以被自定义子类重写,将传入数据强制转换为特定类型。

默认情况下,引发ValueError

根据父类是Mutable类型还是MutableComposite类型,在不同情况下调用此方法。对于前者,它在属性设置操作和 ORM 加载操作期间都会被调用。对于后者,它仅在属性设置操作期间被调用;composite()构造的机制在加载操作期间处理强制转换。

参数:

  • key – 正在设置的 ORM 映射属性的字符串名称。
  • value – 输入值。

返回:

如果无法完成强制转换,则该方法应返回强制转换后的值,或引发ValueError

代码语言:javascript复制
class sqlalchemy.ext.mutable.Mutable

定义透明传播更改事件到父对象的混入。

查看在标量列值上建立可变性中的示例以获取用法信息。

成员

_get_listen_keys(),_listen_on_attribute(),_parents,as_mutable(),associate_with(),associate_with_attribute(),changed(),coerce()

类签名

sqlalchemy.ext.mutable.Mutablesqlalchemy.ext.mutable.MutableBase)

代码语言:javascript复制
classmethod _get_listen_keys(attribute: QueryableAttribute[Any]) → Set[str]

继承自 MutableBase sqlalchemy.ext.mutable.MutableBase._get_listen_keys 方法

给定一个描述符属性,返回一个指示此属性状态变化的属性键的set()

这通常只是set([attribute.key]),但可以被覆盖以提供额外的键。例如,MutableComposite会用与组成复合值的列相关联的属性键来增加这个集合。

在拦截InstanceEvents.refresh()InstanceEvents.refresh_flush()事件时,将查询此集合,这些事件传递了已刷新的属性名称列表;该列表与此集合进行比较,以确定是否需要采取行动。

代码语言:javascript复制
classmethod _listen_on_attribute(attribute: QueryableAttribute[Any], coerce: bool, parent_cls: _ExternalEntityType[Any]) → None

继承自 MutableBase sqlalchemy.ext.mutable.MutableBase._listen_on_attribute 方法

将此类型建立为给定映射描述符的变异监听器。

代码语言:javascript复制
attribute _parents

继承自 MutableBase sqlalchemy.ext.mutable.MutableBase._parents 属性

父对象的InstanceState->父对象上的属性名的字典。

此属性是所谓的“记忆化”属性。它在第一次访问时使用一个新的weakref.WeakKeyDictionary进行初始化,并在后续访问时返回相同的对象。

自版本 1.4 更改:InstanceState现在作为弱字典中的键,而不是实例本身。

代码语言:javascript复制
classmethod as_mutable(sqltype: TypeEngine) → TypeEngine

将 SQL 类型与此可变 Python 类型关联起来。

这将建立侦听器,以检测针对给定类型的 ORM 映射,并向这些映射添加变异事件跟踪器。

类型无条件地作为实例返回,因此可以内联使用as_mutable()

代码语言:javascript复制
Table('mytable', metadata,
    Column('id', Integer, primary_key=True),
    Column('data', MyMutableType.as_mutable(PickleType))
)

请注意,返回的类型始终是一个实例,即使给定一个类,也只有明确声明了该类型实例的列才会接收到额外的仪器设备。

要将特定的可变类型与所有特定类型的所有出现相关联,请使用特定Mutable子类的Mutable.associate_with()类方法来建立全局关联。

警告

此方法建立的侦听器对所有映射器都是全局的,并且会被垃圾回收。只能对应用程序中永久的类型使用as_mutable(),不要与临时类型一起使用,否则这将导致内存使用量无限增长。

代码语言:javascript复制
classmethod associate_with(sqltype: type) → None

将此包装器与未来的给定类型的映射列相关联。

这是一个方便的方法,会自动调用associate_with_attribute

警告

此方法建立的侦听器对所有映射器都是全局的,并且会被垃圾回收。只能对应用程序中永久的类型使用associate_with(),不要与临时类型一起使用,否则这将导致内存使用量无限增长。

代码语言:javascript复制
classmethod associate_with_attribute(attribute: InstrumentedAttribute[_O]) → None

将此类型建立为给定映射描述符的变异侦听器。

代码语言:javascript复制
method changed() → None

子类应该在发生变更事件时调用此方法。

代码语言:javascript复制
classmethod coerce(key: str, value: Any) → Any | None

继承自 MutableBase.coerce() 方法的 MutableBase

给定一个值,将其强制转换为目标类型。

可以由自定义子类重写以将传入数据强制转换为特定类型。

默认情况下,引发ValueError

根据父类是Mutable类型还是MutableComposite类型,在不同的情况下调用此方法。对于前者,在属性设置操作和 ORM 加载操作期间都会调用它。对于后者,在属性设置操作期间才会调用它;composite()构造的机制处理加载操作期间的强制转换。

参数:

  • key – 正在设置的 ORM 映射属性的字符串名称。
  • value – 输入值。

返回:

如果无法完成转换,则该方法应返回转换后的值,或引发ValueError

代码语言:javascript复制
class sqlalchemy.ext.mutable.MutableComposite

混入,定义了将 SQLAlchemy“组合”对象上的变更事件透明传播到其拥有的父对象的机制。

查看在组合上建立可变性中的示例以获取用法信息。

成员

changed()

类签名

sqlalchemy.ext.mutable.MutableCompositesqlalchemy.ext.mutable.MutableBase

代码语言:javascript复制
method changed() → None

子类应在更改事件发生时调用此方法。

代码语言:javascript复制
class sqlalchemy.ext.mutable.MutableDict

一种实现了 Mutable 的字典类型。

MutableDict 对象实现了一个字典,当更改字典的内容时会向底层映射发送更改事件,包括添加或删除值时。

请注意,MutableDict 不会将可变跟踪应用于字典内部的值本身。因此,它不足以解决跟踪对递归字典结构进行深层更改的用例,例如 JSON 结构。要支持此用例,请构建 MutableDict 的子类,该子类提供适当的强制转换,以便将放置在字典中的值也“可变”,并将事件发送到其父结构。

另请参阅

MutableList

MutableSet

成员

clear(), coerce(), pop(), popitem(), setdefault(), update()

类签名

sqlalchemy.ext.mutable.MutableDictsqlalchemy.ext.mutable.Mutablebuiltins.dicttyping.Generic

代码语言:javascript复制
method clear() → None.  Remove all items from D.
代码语言:javascript复制
classmethod coerce(key: str, value: Any) → MutableDict[_KT, _VT] | None

将普通字典转换为此类的实例。

代码语言:javascript复制
method pop(k[, d]) → v, remove specified key and return the corresponding value.

如果找不到键,则在给定默认值的情况下返回;否则,引发 KeyError。

代码语言:javascript复制
method popitem() → Tuple[_KT, _VT]

移除并返回一个(键,值)对作为 2 元组。

以 LIFO(后进先出)顺序返回键值对。如果字典为空,则引发 KeyError。

代码语言:javascript复制
method setdefault(*arg)

如果字典中没有键,则将键插入并将其值设置为默认值。

如果字典中存在键,则返回键的值,否则返回默认值。

代码语言:javascript复制
method update([E, ]**F) → None.  Update D from dict/iterable E and F.

如果 E 存在且具有 .keys() 方法,则执行以下操作:for k in E: D[k] = E[k] 如果 E 存在但缺少 .keys() 方法,则执行以下操作:for k, v in E: D[k] = v 在任一情况下,接下来执行以下操作:for k in F: D[k] = F[k]

代码语言:javascript复制
class sqlalchemy.ext.mutable.MutableList

一种实现了 Mutable 的列表类型。

MutableList对象实现了一个列表,在修改列表内容时会向底层映射发出更改事件,包括添加或删除值时。

注意MutableList不会对列表内部的值本身应用可变跟踪。因此,它不能解决跟踪递归可变结构(例如 JSON 结构)的深层更改的用例。要支持此用例,构建MutableList的子类,提供适当的强制转换以使放置在字典中的值也是“可变的”,并将事件传播到其父结构。

另请参见

MutableDict

MutableSet

成员

append(), clear(), coerce(), extend(), insert(), is_iterable(), is_scalar(), pop(), remove(), reverse(), sort()

类签名

sqlalchemy.ext.mutable.MutableListsqlalchemy.ext.mutable.Mutablebuiltins.listtyping.Generic

代码语言:javascript复制
method append(x: _T) → None

将对象追加到列表末尾。

代码语言:javascript复制
method clear() → None

从列表中删除所有项。

代码语言:javascript复制
classmethod coerce(key: str, value: MutableList[_T] | _T) → MutableList[_T] | None

将普通列表转换为此类的实例。

代码语言:javascript复制
method extend(x: Iterable[_T]) → None

通过从可迭代对象中追加元素来扩展列表。

代码语言:javascript复制
method insert(i: SupportsIndex, x: _T) → None

在索引之前插入对象。

代码语言:javascript复制
method is_iterable(value: _T | Iterable[_T]) → TypeGuard[Iterable[_T]]
代码语言:javascript复制
method is_scalar(value: _T | Iterable[_T]) → TypeGuard[_T]
代码语言:javascript复制
method pop(*arg: SupportsIndex) → _T

移除并返回索引处的项(默认为最后一个)。

如果列表为空或索引超出范围,则引发 IndexError。

代码语言:javascript复制
method remove(i: _T) → None

移除第一次出现的值。

如果值不存在,则引发 ValueError。

代码语言:javascript复制
method reverse() → None

原地反转。

代码语言:javascript复制
method sort(**kw: Any) → None

对列表进行升序排序并返回 None。

排序是原地进行的(即修改列表本身)并且是稳定的(即保持两个相等元素的顺序)。

如果给定了键函数,则将其一次应用于每个列表项并根据其函数值升序或降序排序。

反转标志可以设置为按降序排序。

代码语言:javascript复制
class sqlalchemy.ext.mutable.MutableSet

实现了Mutable的集合类型。

MutableSet 对象实现了一个集合,当集合的内容发生更改时,将向底层映射发出更改事件,包括添加或删除值时。

请注意,MutableSet 不会对集合中值本身应用可变跟踪。因此,它不是跟踪递归可变结构的深层更改的足够解决方案。为了支持这种用例,请构建一个MutableSet的子类,该子类提供适当的强制转换,使放置在字典中的值也是“可变的”,并向其父结构发出事件。

另请参阅

MutableDict

MutableList

成员

add(), clear(), coerce(), difference_update(), discard(), intersection_update(), pop(), remove(), symmetric_difference_update(), update()

类签名

sqlalchemy.ext.mutable.MutableSetsqlalchemy.ext.mutable.Mutablebuiltins.settyping.Generic

代码语言:javascript复制
method add(elem: _T) → None

向集合添加一个元素。

如果元素已经存在,则不起作用。

代码语言:javascript复制
method clear() → None

从此集合中移除所有元素。

代码语言:javascript复制
classmethod coerce(index: str, value: Any) → MutableSet[_T] | None

将普通集合转换为此类的实例。

代码语言:javascript复制
method difference_update(*arg: Iterable[Any]) → None

从此集合中删除另一个集合的所有元素。

代码语言:javascript复制
method discard(elem: _T) → None

如果元素是成员,则从集合中删除一个元素。

如果元素不是成员,则不执行任何操作。

代码语言:javascript复制
method intersection_update(*arg: Iterable[Any]) → None

使用自身与另一个集合的交集更新集合。

代码语言:javascript复制
method pop(*arg: Any) → _T

移除并返回一个任意的集合元素。如果集合为空,则引发 KeyError。

代码语言:javascript复制
method remove(elem: _T) → None

从集合中删除一个元素;它必须是成员。

如果元素不是成员,则引发 KeyError。

代码语言:javascript复制
method symmetric_difference_update(*arg: Iterable[_T]) → None

使用自身与另一个集合的对称差更新集合。

代码语言:javascript复制
method update(*arg: Iterable[_T]) → None

使用自身与其他集合的并集更新集合。

在标量列值上建立可变性

“可变”结构的典型示例是 Python 字典。在 SQL 数据类型对象中介绍的示例中,我们从自定义类型开始,该类型在持久化之前将 Python 字典编组为 JSON 字符串:

代码语言:javascript复制
from sqlalchemy.types import TypeDecorator, VARCHAR
import json

class JSONEncodedDict(TypeDecorator):
    "Represents an immutable structure as a json-encoded string."

    impl = VARCHAR

    def process_bind_param(self, value, dialect):
        if value is not None:
            value = json.dumps(value)
        return value

    def process_result_value(self, value, dialect):
        if value is not None:
            value = json.loads(value)
        return value

使用json仅用于示例目的。sqlalchemy.ext.mutable 扩展可以与任何目标 Python 类型可能是可变的类型一起使用,包括PickleTypeARRAY等。

当使用sqlalchemy.ext.mutable 扩展时,值本身跟踪所有引用它的父对象。下面,我们展示了一个简单版本的MutableDict字典对象,它将Mutable mixin 应用于普通的 Python 字典:

代码语言:javascript复制
from sqlalchemy.ext.mutable import Mutable

class MutableDict(Mutable, dict):
    @classmethod
    def coerce(cls, key, value):
        "Convert plain dictionaries to MutableDict."

        if not isinstance(value, MutableDict):
            if isinstance(value, dict):
                return MutableDict(value)

            # this call will raise ValueError
            return Mutable.coerce(key, value)
        else:
            return value

    def __setitem__(self, key, value):
        "Detect dictionary set events and emit change events."

        dict.__setitem__(self, key, value)
        self.changed()

    def __delitem__(self, key):
        "Detect dictionary del events and emit change events."

        dict.__delitem__(self, key)
        self.changed()

上述字典类采用了子类化 Python 内置的dict的方法,以产生一个 dict 子类,通过__setitem__路由所有的变异事件。这种方法还有变体,比如子类化UserDict.UserDictcollections.MutableMapping;对于这个示例很重要的部分是,每当对数据结构进行就地更改时,都会调用Mutable.changed()方法。

我们还重新定义了Mutable.coerce() 方法,用于将不是MutableDict实例的任何值转换为适当的类型,比如json模块返回的普通字典。定义这个方法是可选的;我们也可以创建我们的JSONEncodedDict,使其始终返回MutableDict的实例,并确保所有调用代码都明确使用MutableDict。当未覆盖Mutable.coerce()时,应用于父对象的任何不是可变类型实例的值将引发ValueError

我们的新MutableDict类型提供了一个类方法Mutable.as_mutable(),我们可以在列元数据中使用它与类型关联。这个方法获取给定的类型对象或类,并关联一个监听器,将检测到所有将来映射到该类型的映射,应用事件监听仪器到映射的属性。例如,使用经典表元数据:

代码语言:javascript复制
from sqlalchemy import Table, Column, Integer

my_data = Table('my_data', metadata,
    Column('id', Integer, primary_key=True),
    Column('data', MutableDict.as_mutable(JSONEncodedDict))
)

上面,Mutable.as_mutable() 返回一个JSONEncodedDict的实例(如果类型对象尚未是一个实例),它将拦截任何映射到该类型的属性。下面我们建立一个简单的映射到my_data表:

代码语言:javascript复制
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column

class Base(DeclarativeBase):
    pass

class MyDataClass(Base):
    __tablename__ = 'my_data'
    id: Mapped[int] = mapped_column(primary_key=True)
    data: Mapped[dict[str, str]] = mapped_column(MutableDict.as_mutable(JSONEncodedDict))

MyDataClass.data 成员现在将被通知其值的原地更改。

MyDataClass.data 成员的任何原地更改都将标记父对象的属性为“脏”:

代码语言:javascript复制
>>> from sqlalchemy.orm import Session

>>> sess = Session(some_engine)
>>> m1 = MyDataClass(data={'value1':'foo'})
>>> sess.add(m1)
>>> sess.commit()

>>> m1.data['value1'] = 'bar'
>>> assert m1 in sess.dirty
True

MutableDict 可以通过一个步骤与所有未来的 JSONEncodedDict 实例关联,使用 Mutable.associate_with()。这类似于 Mutable.as_mutable(),但它将无条件拦截所有映射中 MutableDict 的所有出现,而无需单独声明它:

代码语言:javascript复制
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column

MutableDict.associate_with(JSONEncodedDict)

class Base(DeclarativeBase):
    pass

class MyDataClass(Base):
    __tablename__ = 'my_data'
    id: Mapped[int] = mapped_column(primary_key=True)
    data: Mapped[dict[str, str]] = mapped_column(JSONEncodedDict)
支持 Pickling

sqlalchemy.ext.mutable 扩展的关键在于在值对象上放置一个 weakref.WeakKeyDictionary,它存储了父映射对象的映射,键为它们与该值相关联的属性名。 WeakKeyDictionary 对象不可 pickle,因为它们包含弱引用和函数回调。在我们的情况下,这是一件好事,因为如果这个字典是可 pickle 的,它可能会导致独立于父对象上下文的值对象的 pickle 大小过大。在这里,开发者的责任仅仅是提供一个 __getstate__ 方法,从 pickle 流中排除 MutableBase._parents() 集合:

代码语言:javascript复制
class MyMutableType(Mutable):
    def __getstate__(self):
        d = self.__dict__.copy()
        d.pop('_parents', None)
        return d

对于我们的字典示例,我们需要返回字典本身的内容(并在 setstate 中还原它们):

代码语言:javascript复制
class MutableDict(Mutable, dict):
    # ....

    def __getstate__(self):
        return dict(self)

    def __setstate__(self, state):
        self.update(state)

如果我们的可变值对象被 pickle,而它附加到一个或多个也是 pickle 的父对象上,Mutable mixin 将在每个值对象上重新建立 Mutable._parents 集合,因为拥有父对象本身被 unpickle。

接收事件

当一个可变标量发出变更事件时,AttributeEvents.modified() 事件处理程序可以用于接收事件。当在可变扩展内部调用 flag_modified() 函数时,将调用此事件处理程序:

代码语言:javascript复制
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy import event

class Base(DeclarativeBase):
    pass

class MyDataClass(Base):
    __tablename__ = 'my_data'
    id: Mapped[int] = mapped_column(primary_key=True)
    data: Mapped[dict[str, str]] = mapped_column(MutableDict.as_mutable(JSONEncodedDict))

@event.listens_for(MyDataClass.data, "modified")
def modified_json(instance, initiator):
    print("json value modified:", instance.data)
支持 Pickling

sqlalchemy.ext.mutable 扩展的关键在于在值对象上放置一个 weakref.WeakKeyDictionary,该字典存储父映射对象的映射,以属性名称为键,这些父映射对象与该值相关联。由于 WeakKeyDictionary 对象包含弱引用和函数回调,因此它们不可 picklable。在我们的情况下,这是一件好事,因为如果这个字典是可 pickle 的,那么它可能会导致我们的值对象的 pickle 大小过大,这些值对象是在不涉及父对象的情况下 pickle 的。开发者在这里的责任只是提供一个 __getstate__ 方法,该方法从 pickle 流中排除了 MutableBase._parents() 集合:

代码语言:javascript复制
class MyMutableType(Mutable):
    def __getstate__(self):
        d = self.__dict__.copy()
        d.pop('_parents', None)
        return d

对于我们的字典示例,我们需要返回字典本身的内容(并在 setstate 中还原它们):

代码语言:javascript复制
class MutableDict(Mutable, dict):
    # ....

    def __getstate__(self):
        return dict(self)

    def __setstate__(self, state):
        self.update(state)

在我们可变的值对象作为 pickle 对象时,如果它附加在一个或多个也是 pickle 的父对象上,Mutable mixin 将在每个值对象上重新建立 Mutable._parents 集合,因为拥有父对象的本身会被 unpickle。

接收事件

AttributeEvents.modified() 事件处理程序可用于在可变标量发出更改事件时接收事件。当在可变扩展中调用 flag_modified() 函数时,将调用此事件处理程序:

代码语言:javascript复制
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy import event

class Base(DeclarativeBase):
    pass

class MyDataClass(Base):
    __tablename__ = 'my_data'
    id: Mapped[int] = mapped_column(primary_key=True)
    data: Mapped[dict[str, str]] = mapped_column(MutableDict.as_mutable(JSONEncodedDict))

@event.listens_for(MyDataClass.data, "modified")
def modified_json(instance, initiator):
    print("json value modified:", instance.data)

确立组合物的可变性

组合物是 ORM 的一种特殊功能,它允许将单个标量属性分配给一个对象值,该对象值表示从底层映射表中的一个或多个列中“组合”出的信息。通常的例子是几何“点”,并在 Composite Column Types 中介绍。

Mutable类似,用户定义的复合类作为一个混合类继承MutableComposite,通过MutableComposite.changed()方法检测并传递更改事件给其父类。对于复合类,通常是通过使用特殊的 Python 方法__setattr__()来进行检测。在下面的示例中,我们扩展了复合列类型中介绍的Point类,将MutableComposite包含在其基类中,并通过__setattr__将属性设置事件路由到MutableComposite.changed()方法:

代码语言:javascript复制
import dataclasses
from sqlalchemy.ext.mutable import MutableComposite

@dataclasses.dataclass
class Point(MutableComposite):
    x: int
    y: int

    def __setattr__(self, key, value):
        "Intercept set events"

        # set the attribute
        object.__setattr__(self, key, value)

        # alert all parents to the change
        self.changed()

MutableComposite类利用类映射事件自动为任何指定我们的Point类型的composite()的使用建立监听器。下面,当Point映射到Vertex类时,将建立监听器,这些监听器将把Point对象的更改事件路由到Vertex.startVertex.end属性中的每一个:

代码语言:javascript复制
from sqlalchemy.orm import DeclarativeBase, Mapped
from sqlalchemy.orm import composite, mapped_column

class Base(DeclarativeBase):
    pass

class Vertex(Base):
    __tablename__ = "vertices"

    id: Mapped[int] = mapped_column(primary_key=True)

    start: Mapped[Point] = composite(mapped_column("x1"), mapped_column("y1"))
    end: Mapped[Point] = composite(mapped_column("x2"), mapped_column("y2"))

    def __repr__(self):
        return f"Vertex(start={self.start}, end={self.end})"

Vertex.startVertex.end成员的任何就地更改都会在父对象上标记属性为“脏”:

代码语言:javascript复制
>>> from sqlalchemy.orm import Session
>>> sess = Session(engine)
>>> v1 = Vertex(start=Point(3, 4), end=Point(12, 15))
>>> sess.add(v1)
sql>>> sess.flush()
BEGIN  (implicit)
INSERT  INTO  vertices  (x1,  y1,  x2,  y2)  VALUES  (?,  ?,  ?,  ?)
[...]  (3,  4,  12,  15)
>>> v1.end.x = 8
>>> assert v1 in sess.dirty
True
sql>>> sess.commit()
UPDATE  vertices  SET  x2=?  WHERE  vertices.id  =  ?
[...]  (8,  1)
COMMIT 
强制可变复合类型

在复合类型上也支持MutableBase.coerce()方法。对于MutableCompositeMutableBase.coerce()方法仅在属性设置操作中调用,而不是在加载操作中调用。覆盖MutableBase.coerce()方法基本上等同于为使用自定义复合类型的所有属性使用validates()验证程序:

代码语言:javascript复制
@dataclasses.dataclass
class Point(MutableComposite):
    # other Point methods
    # ...

    def coerce(cls, key, value):
        if isinstance(value, tuple):
            value = Point(*value)
        elif not isinstance(value, Point):
            raise ValueError("tuple or Point expected")
        return value
支持 Pickling

Mutable类似,MutableComposite辅助类使用了通过MutableBase._parents()属性获得的weakref.WeakKeyDictionary,该字典不可 pickle。如果我们需要 pickle Point 或其拥有类 Vertex 的实例,至少需要定义一个不包含 _parents 字典的__getstate__。下面我们定义了Point类的最小形式的__getstate____setstate__

代码语言:javascript复制
@dataclasses.dataclass
class Point(MutableComposite):
    # ...

    def __getstate__(self):
        return self.x, self.y

    def __setstate__(self, state):
        self.x, self.y = state

Mutable类似,MutableComposite增强了父对象的对象关系状态的 pickling 过程,以便将MutableBase._parents()集合还原为所有Point对象。

强制转换可变复合类型

MutableBase.coerce()方法也支持复合类型。在MutableComposite的情况下,MutableBase.coerce()方法仅在属性设置操作而非加载操作时调用。覆盖MutableBase.coerce()方法基本上等同于对使用自定义复合类型的所有属性使用validates()验证程序:

代码语言:javascript复制
@dataclasses.dataclass
class Point(MutableComposite):
    # other Point methods
    # ...

    def coerce(cls, key, value):
        if isinstance(value, tuple):
            value = Point(*value)
        elif not isinstance(value, Point):
            raise ValueError("tuple or Point expected")
        return value
支持 Pickling

Mutable类似,MutableComposite辅助类使用了通过MutableBase._parents()属性获得的weakref.WeakKeyDictionary,该字典不可 pickle。如果我们需要 pickle Point 或其拥有类 Vertex 的实例,至少需要定义一个不包含 _parents 字典的__getstate__。下面我们定义了Point类的最小形式的__getstate____setstate__

代码语言:javascript复制
@dataclasses.dataclass
class Point(MutableComposite):
    # ...

    def __getstate__(self):
        return self.x, self.y

    def __setstate__(self, state):
        self.x, self.y = state

Mutable一样,MutableComposite增强了父对象的对象关系状态的 pickling 过程,以便将MutableBase._parents()集合还原为所有Point对象。

API 参考

对象名称

描述

Mutable

定义更改事件透明传播到父对象的混合类。

MutableBase

Mutable和MutableComposite的通用基类。

MutableComposite

定义 SQLAlchemy “composite” 对象上的更改事件透明传播到其拥有的父对象或父对象的混合类。

MutableDict

一个实现Mutable的字典类型。

MutableList

一个实现Mutable的列表类型。

MutableSet

一个实现Mutable的集合类型。

代码语言:javascript复制
class sqlalchemy.ext.mutable.MutableBase

成员

_parents, coerce()

MutableMutableComposite的通用基类。

代码语言:javascript复制
attribute _parents

父对象的InstanceState字典->父对象上的属性名称。

此属性是所谓的“记忆化”属性。第一次访问时,它会用一个新的weakref.WeakKeyDictionary初始化自己,并在后续访问时返回相同的对象。

在 1.4 版中更改:InstanceState现在被用作弱字典中的键,而不是实例本身。

代码语言:javascript复制
classmethod coerce(key: str, value: Any) → Any | None

给定一个值,将其强制转换为目标类型。

可以被自定义子类覆盖,将传入数据强制转换为特定类型。

默认情况下,引发ValueError

根据父类是Mutable类型还是MutableComposite类型,在不同的情况下调用此方法。对于前者,它在属性集操作和 ORM 加载操作期间都会被调用。对于后者,它仅在属性集操作期间被调用;composite()构造的机制在加载操作期间处理强制转换。

参数:

  • key – 正在设置的 ORM 映射属性的字符串名称。
  • value – 传入的值。

返回:

如果无法完成强制转换,该方法应返回强制转换后的值,或引发ValueError

代码语言:javascript复制
class sqlalchemy.ext.mutable.Mutable

定义将更改事件透明传播到父对象的混合类。

查看在标量列值上建立可变性中的示例以获取用法信息。

成员

_get_listen_keys(), _listen_on_attribute(), _parents, as_mutable(), associate_with(), associate_with_attribute(), changed(), coerce()

类签名

sqlalchemy.ext.mutable.Mutablesqlalchemy.ext.mutable.MutableBase

代码语言:javascript复制
classmethod _get_listen_keys(attribute: QueryableAttribute[Any]) → Set[str]

继承自 MutableBase sqlalchemy.ext.mutable.MutableBase._get_listen_keys 方法

给定一个描述符属性,返回指示此属性状态变化的属性键的set()

通常只是set([attribute.key]),但可以被覆盖以提供额外的键。例如,MutableComposite 会用包含组合值的列相关联的属性键来增加这个集合。

在拦截InstanceEvents.refresh()InstanceEvents.refresh_flush()事件时,会查询此集合,这些事件会传递一个已刷新的属性名称列表;该列表将与此集合进行比较,以确定是否需要采取行动。

代码语言:javascript复制
classmethod _listen_on_attribute(attribute: QueryableAttribute[Any], coerce: bool, parent_cls: _ExternalEntityType[Any]) → None

继承自 MutableBasesqlalchemy.ext.mutable.MutableBase._listen_on_attribute 方法

将此类型作为给定映射描述符的变异监听器。

代码语言:javascript复制
attribute _parents

继承自 MutableBasesqlalchemy.ext.mutable.MutableBase._parents 属性

父对象的InstanceState->父对象上的属性名称的字典。

此属性是所谓的“记忆化”属性。它在首次访问时使用一个新的weakref.WeakKeyDictionary进行初始化,并在后续访问时返回相同的对象。

在 1.4 版本中更改:现在使用InstanceState作为弱字典中的键,而不是实例本身。

代码语言:javascript复制
classmethod as_mutable(sqltype: TypeEngine) → TypeEngine

将 SQL 类型与此可变 Python 类型关联。

这将建立监听器,用于检测针对给定类型的 ORM 映射,向这些映射添加变异事件跟踪器。

该类型无条件地作为一个实例返回,以便可以内联使用as_mutable()

代码语言:javascript复制
Table('mytable', metadata,
    Column('id', Integer, primary_key=True),
    Column('data', MyMutableType.as_mutable(PickleType))
)

请注意,返回的类型始终是一个实例,即使给定一个类,也只有明确声明了该类型实例的列才会接收到额外的仪器化。

要将特定的可变类型与特定类型的所有出现关联起来,请使用Mutable.associate_with()类方法的特定Mutable子类来建立全局关联。

警告

此方法建立的监听器是全局的,适用于所有映射器,并且会被垃圾回收。只能对应用程序中永久的类型使用as_mutable(),而不是临时类型,否则会导致内存使用量无限增长。

代码语言:javascript复制
classmethod associate_with(sqltype: type) → None

将此包装器与将来的给定类型的映射列关联起来。

这是一个方便的方法,会自动调用associate_with_attribute

警告

该方法建立的监听器是全局的,适用于所有映射器,并且会被垃圾回收。只能对应用程序中永久的类型使用associate_with(),而不是临时类型,否则会导致内存使用量无限增长。

代码语言:javascript复制
classmethod associate_with_attribute(attribute: InstrumentedAttribute[_O]) → None

将此类型作为给定映射描述符的变异监听器。

代码语言:javascript复制
method changed() → None

子类在发生更改事件时应调用此方法。

代码语言:javascript复制
classmethod coerce(key: str, value: Any) → Any | None

继承自 MutableBase.coerce() 方法MutableBase

给定一个值,将其强制转换为目标类型。

可以被自定义子类重写以将传入的数据强制转换为特定类型。

默认情况下,引发 ValueError

此方法在不同的情况下被调用,具体取决于父类是 Mutable 类型还是 MutableComposite 类型。在前者的情况下,它将在属性设置操作以及 ORM 加载操作期间被调用。对于后者,它仅在属性设置操作期间被调用;composite() 构造的机制在加载操作期间处理强制转换。

参数:

  • key – 被设置的 ORM 映射属性的字符串名称。
  • value – 传入的值。

返回:

如果无法完成强制转换,则该方法应返回强制转换后的值,或引发 ValueError

代码语言:javascript复制
class sqlalchemy.ext.mutable.MutableComposite

定义了对 SQLAlchemy “组合”对象的更改事件的透明传播的混合类到其拥有的父对象。

请参阅 在组合上建立可变性 中的示例以获取用法信息。

成员

changed()

类签名

class sqlalchemy.ext.mutable.MutableComposite (sqlalchemy.ext.mutable.MutableBase)

代码语言:javascript复制
method changed() → None

子类应在更改事件发生时调用此方法。

代码语言:javascript复制
class sqlalchemy.ext.mutable.MutableDict

实现了 Mutable 的字典类型。

MutableDict 对象实现了一个字典,在字典内容发生更改时将向基础映射发出更改事件,包括添加或移除值时。

请注意,MutableDict 不会 对字典内部的值本身应用可变跟踪。因此,它不足以解决跟踪递归字典结构(例如 JSON 结构)的深层更改的用例。要支持此用例,请构建一个 MutableDict 的子类,以提供适当的强制转换,以便放置在字典中的值也是“可变的”,并将事件传播到其父结构。

另请参见

MutableList

MutableSet

成员

clear(), coerce(), pop(), popitem(), setdefault(), update()

类签名

sqlalchemy.ext.mutable.MutableDict (sqlalchemy.ext.mutable.Mutable, builtins.dict, typing.Generic)

代码语言:javascript复制
method clear() → None.  Remove all items from D.
代码语言:javascript复制
classmethod coerce(key: str, value: Any) → MutableDict[_KT, _VT] | None

将普通字典转换为此类的实例。

代码语言:javascript复制
method pop(k[, d]) → v, remove specified key and return the corresponding value.

如果找不到键,则返回默认值(如果给定);否则,引发 KeyError。

代码语言:javascript复制
method popitem() → Tuple[_KT, _VT]

移除并返回一个(key, value)对作为 2 元组。

键值对以 LIFO(后进先出)顺序返回。如果字典为空,则引发 KeyError。

代码语言:javascript复制
method setdefault(*arg)

如果键不在字典中,则将键插入并设置默认值。

如果键在字典中,则返回键的值,否则返回默认值。

代码语言:javascript复制
method update([E, ]**F) → None.  Update D from dict/iterable E and F.

如果 E 存在并且具有.keys()方法,则执行: for k in E: D[k] = E[k] 如果 E 存在但缺少.keys()方法,则执行: for k, v in E: D[k] = v 在任一情况下,接下来执行: for k in F: D[k] = F[k]

代码语言:javascript复制
class sqlalchemy.ext.mutable.MutableList

一个实现了Mutable的列表类型。

MutableList 对象实现了一个列表,当列表的内容被更改时,包括添加或删除值时,将向底层映射发送更改事件。

请注意,MutableList 不会对列表内部的值本身应用可变跟踪。因此,它不是跟踪对递归可变结构进行深层更改的使用案例的充分解决方案,例如 JSON 结构。为支持此使用案例,请构建MutableList的子类,该子类提供适当的强制转换以使放置在字典中的值也是“可变的”,并将事件发送到其父结构。

另请参阅

MutableDict

MutableSet

成员

append(), clear(), coerce(), extend(), insert(), is_iterable(), is_scalar(), pop(), remove(), reverse(), sort()

类签名

sqlalchemy.ext.mutable.MutableListsqlalchemy.ext.mutable.Mutablebuiltins.listtyping.Generic

代码语言:javascript复制
method append(x: _T) → None

将对象追加到列表末尾。

代码语言:javascript复制
method clear() → None

从列表中删除所有项。

代码语言:javascript复制
classmethod coerce(key: str, value: MutableList[_T] | _T) → MutableList[_T] | None

将普通列表转换为此类的实例。

代码语言:javascript复制
method extend(x: Iterable[_T]) → None

通过将来自可迭代对象的元素附加到列表来扩展列表。

代码语言:javascript复制
method insert(i: SupportsIndex, x: _T) → None

在索引之前插入对象。

代码语言:javascript复制
method is_iterable(value: _T | Iterable[_T]) → TypeGuard[Iterable[_T]]
代码语言:javascript复制
method is_scalar(value: _T | Iterable[_T]) → TypeGuard[_T]
代码语言:javascript复制
method pop(*arg: SupportsIndex) → _T

删除并返回索引处的项(默认为最后一个)。

如果列表为空或索引超出范围,则引发 IndexError。

代码语言:javascript复制
method remove(i: _T) → None

删除值的第一个出现。

如果值不存在,则引发 ValueError。

代码语言:javascript复制
method reverse() → None

就地反转。

代码语言:javascript复制
method sort(**kw: Any) → None

将列表按升序排序并返回 None。

排序是原地进行的(即列表本身被修改)并且稳定的(即保持两个相等元素的顺序不变)。

如果给定了键函数,则将其应用于每个列表项一次,并根据其函数值按升序或降序对它们进行排序。

反转标志可以设置为按降序排序。

代码语言:javascript复制
class sqlalchemy.ext.mutable.MutableSet

实现了Mutable的集合类型。

MutableSet 对象实现了一个集合,当集合的内容发生变化时,包括添加或移除值时,会向底层映射发送更改事件。

注意,MutableSet 不会对集合内部的值本身应用可变跟踪。因此,它不是跟踪对递归可变结构进行深层更改的足够解决方案。为了支持这种用例,构建一个MutableSet的子类,提供适当的强制转换,以便放置在字典中的值也是“可变的”,并向它们的父结构发出事件。

另请参阅

MutableDict

MutableList

成员

add(), clear(), coerce(), difference_update(), discard(), intersection_update(), pop(), remove(), symmetric_difference_update(), update()

类签名

sqlalchemy.ext.mutable.MutableSetsqlalchemy.ext.mutable.Mutable, builtins.set, typing.Generic)

代码语言:javascript复制
method add(elem: _T) → None

向集合添加一个元素。

如果元素已经存在,则不产生任何效果。

代码语言:javascript复制
method clear() → None

从此集合中移除所有元素。

代码语言:javascript复制
classmethod coerce(index: str, value: Any) → MutableSet[_T] | None

将普通集合转换为此类的实例。

代码语言:javascript复制
method difference_update(*arg: Iterable[Any]) → None

从此集合中移除另一个集合的所有元素。

代码语言:javascript复制
method discard(elem: _T) → None

如果元素是集合的成员,则从集合中移除一个元素。

如果元素不是成员,则不执行任何操作。

代码语言:javascript复制
method intersection_update(*arg: Iterable[Any]) → None

使用自身和另一个集合的交集更新集合。

代码语言:javascript复制
method pop(*arg: Any) → _T

移除并返回任意集合元素。如果集合为空,则引发 KeyError。

代码语言:javascript复制
method remove(elem: _T) → None

从集合中移除一个元素;它必须是成员。

如果元素不是成员,则引发 KeyError。

代码语言:javascript复制
method symmetric_difference_update(*arg: Iterable[_T]) → None

使用自身和另一个集合的对称差集更新集合。

代码语言:javascript复制
method update(*arg: Iterable[_T]) → None

使用自身和其他集合的并集更新集合。 tableList

代码语言:javascript复制
一个实现了`Mutable`的列表类型。

`MutableList` 对象实现了一个列表,当列表的内容被更改时,包括添加或删除值时,将向底层映射发送更改事件。

请注意,`MutableList` 不会对列表内部的*值本身*应用可变跟踪。因此,它不是跟踪对*递归*可变结构进行深层更改的使用案例的充分解决方案,例如 JSON 结构。为支持此使用案例,请构建`MutableList`的子类,该子类提供适当的强制转换以使放置在字典中的值也是“可变的”,并将事件发送到其父结构。

另请参阅

`MutableDict`

`MutableSet`

**成员**

append(), clear(), coerce(), extend(), insert(), is_iterable(), is_scalar(), pop(), remove(), reverse(), sort()

**类签名**

类`sqlalchemy.ext.mutable.MutableList`(`sqlalchemy.ext.mutable.Mutable`,`builtins.list`,`typing.Generic`)

```py
method append(x: _T) → None

将对象追加到列表末尾。

代码语言:javascript复制
method clear() → None

从列表中删除所有项。

代码语言:javascript复制
classmethod coerce(key: str, value: MutableList[_T] | _T) → MutableList[_T] | None

将普通列表转换为此类的实例。

代码语言:javascript复制
method extend(x: Iterable[_T]) → None

通过将来自可迭代对象的元素附加到列表来扩展列表。

代码语言:javascript复制
method insert(i: SupportsIndex, x: _T) → None

在索引之前插入对象。

代码语言:javascript复制
method is_iterable(value: _T | Iterable[_T]) → TypeGuard[Iterable[_T]]
代码语言:javascript复制
method is_scalar(value: _T | Iterable[_T]) → TypeGuard[_T]
代码语言:javascript复制
method pop(*arg: SupportsIndex) → _T

删除并返回索引处的项(默认为最后一个)。

如果列表为空或索引超出范围,则引发 IndexError。

代码语言:javascript复制
method remove(i: _T) → None

删除值的第一个出现。

如果值不存在,则引发 ValueError。

代码语言:javascript复制
method reverse() → None

就地反转。

代码语言:javascript复制
method sort(**kw: Any) → None

将列表按升序排序并返回 None。

排序是原地进行的(即列表本身被修改)并且稳定的(即保持两个相等元素的顺序不变)。

如果给定了键函数,则将其应用于每个列表项一次,并根据其函数值按升序或降序对它们进行排序。

反转标志可以设置为按降序排序。

代码语言:javascript复制
class sqlalchemy.ext.mutable.MutableSet

实现了Mutable的集合类型。

MutableSet 对象实现了一个集合,当集合的内容发生变化时,包括添加或移除值时,会向底层映射发送更改事件。

注意,MutableSet 不会对集合内部的值本身应用可变跟踪。因此,它不是跟踪对递归可变结构进行深层更改的足够解决方案。为了支持这种用例,构建一个MutableSet的子类,提供适当的强制转换,以便放置在字典中的值也是“可变的”,并向它们的父结构发出事件。

另请参阅

MutableDict

MutableList

成员

add(), clear(), coerce(), difference_update(), discard(), intersection_update(), pop(), remove(), symmetric_difference_update(), update()

类签名

sqlalchemy.ext.mutable.MutableSetsqlalchemy.ext.mutable.Mutable, builtins.set, typing.Generic)

代码语言:javascript复制
method add(elem: _T) → None

向集合添加一个元素。

如果元素已经存在,则不产生任何效果。

代码语言:javascript复制
method clear() → None

从此集合中移除所有元素。

代码语言:javascript复制
classmethod coerce(index: str, value: Any) → MutableSet[_T] | None

将普通集合转换为此类的实例。

代码语言:javascript复制
method difference_update(*arg: Iterable[Any]) → None

从此集合中移除另一个集合的所有元素。

代码语言:javascript复制
method discard(elem: _T) → None

如果元素是集合的成员,则从集合中移除一个元素。

如果元素不是成员,则不执行任何操作。

代码语言:javascript复制
method intersection_update(*arg: Iterable[Any]) → None

使用自身和另一个集合的交集更新集合。

代码语言:javascript复制
method pop(*arg: Any) → _T

移除并返回任意集合元素。如果集合为空,则引发 KeyError。

代码语言:javascript复制
method remove(elem: _T) → None

从集合中移除一个元素;它必须是成员。

如果元素不是成员,则引发 KeyError。

代码语言:javascript复制
method symmetric_difference_update(*arg: Iterable[_T]) → None

使用自身和另一个集合的对称差集更新集合。

代码语言:javascript复制
method update(*arg: Iterable[_T]) → None

使用自身和其他集合的并集更新集合。

0 人点赞