声明式映射风格

Declarative Mapping Styles

正如在 声明式映射 中介绍的那样, 声明式映射 是现代 SQLAlchemy 中构建映射的典型方式。本节将概述可用于声明式映射器配置的形式。

As introduced at 声明式映射, the Declarative Mapping is the typical way that mappings are constructed in modern SQLAlchemy. This section will provide an overview of forms that may be used for Declarative mapper configuration.

使用声明性基类

Using a Declarative Base Class

最常见的方法是通过继承 DeclarativeBase 超类生成“声明式基类”:

from sqlalchemy.orm import DeclarativeBase


# 声明式基类
class Base(DeclarativeBase):
    pass

声明式基类也可以通过将现有的 registry 分配为名为 registry 的类变量来创建:

from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import registry

reg = registry()


# 声明式基类
class Base(DeclarativeBase):
    registry = reg

在 2.0 版本发生变更: The DeclarativeBase 超类取代了 declarative_base() 函数和 registry.generate_base() 方法的使用;超类方法与 PEP 484 工具集成,无需使用插件。有关迁移说明,请参见 ORM Declarative Models

使用声明式基类,可以将新映射类声明为基类的子类:

from datetime import datetime
from typing import List
from typing import Optional

from sqlalchemy import ForeignKey
from sqlalchemy import func
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "user"

    id = mapped_column(Integer, primary_key=True)
    name: Mapped[str]
    fullname: Mapped[Optional[str]]
    nickname: Mapped[Optional[str]] = mapped_column(String(64))
    create_date: Mapped[datetime] = mapped_column(insert_default=func.now())

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


class Address(Base):
    __tablename__ = "address"

    id = mapped_column(Integer, primary_key=True)
    user_id = mapped_column(ForeignKey("user.id"))
    email_address: Mapped[str]

    user: Mapped["User"] = relationship(back_populates="addresses")

以上, Base 类作为要映射的新类的基础,如上所述,构造了新映射类 UserAddress

对于构造的每个子类,类的主体遵循声明式映射方法,该方法在幕后定义了 TableMapper 对象,组成完整的映射。

参见

声明式的表配置 - 描述了如何指定要生成的映射 Table 的组件,包括关于 mapped_column() 构造的使用及其如何与 Mapped 注释类型交互的说明和选项

使用声明式映射器配置 - 描述了声明式中ORM映射器配置的所有其他方面,包括 relationship() 配置、SQL表达式和 Mapper 参数

The most common approach is to generate a “Declarative Base” class by subclassing the DeclarativeBase superclass:

from sqlalchemy.orm import DeclarativeBase


# declarative base class
class Base(DeclarativeBase):
    pass

The Declarative Base class may also be created given an existing registry by assigning it as a class variable named registry:

from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import registry

reg = registry()


# declarative base class
class Base(DeclarativeBase):
    registry = reg

在 2.0 版本发生变更: The DeclarativeBase superclass supersedes

the use of the declarative_base() function and registry.generate_base() methods; the superclass approach integrates with PEP 484 tools without the use of plugins. See ORM Declarative Models for migration notes.

With the declarative base class, new mapped classes are declared as subclasses of the base:

from datetime import datetime
from typing import List
from typing import Optional

from sqlalchemy import ForeignKey
from sqlalchemy import func
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "user"

    id = mapped_column(Integer, primary_key=True)
    name: Mapped[str]
    fullname: Mapped[Optional[str]]
    nickname: Mapped[Optional[str]] = mapped_column(String(64))
    create_date: Mapped[datetime] = mapped_column(insert_default=func.now())

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


class Address(Base):
    __tablename__ = "address"

    id = mapped_column(Integer, primary_key=True)
    user_id = mapped_column(ForeignKey("user.id"))
    email_address: Mapped[str]

    user: Mapped["User"] = relationship(back_populates="addresses")

Above, the Base class serves as a base for new classes that are to be mapped, as above new mapped classes User and Address are constructed.

For each subclass constructed, the body of the class then follows the declarative mapping approach which defines both a Table as well as a Mapper object behind the scenes which comprise a full mapping.

参见

声明式的表配置 - describes how to specify the components of the mapped Table to be generated, including notes and options on the use of the mapped_column() construct and how it interacts with the Mapped annotation type

使用声明式映射器配置 - describes all other aspects of ORM mapper configuration within Declarative including relationship() configuration, SQL expressions and Mapper parameters

使用装饰器进行声明性映射(无声明性基类)

Declarative Mapping using a Decorator (no declarative base)

作为使用 “声明式基类” 的替代方法,可以使用类似于 “经典” 映射的命令式技术,或更简洁地使用装饰器,将声明式映射显式应用于类。registry.mapped() 函数是一个类装饰器,可以应用于没有层次结构的任何Python类。Python类在其他方面通常以声明式风格配置。

下面的示例使用 registry.mapped() 装饰器而不是 DeclarativeBase 超类设置了与上一节中相同的映射:

from datetime import datetime
from typing import List
from typing import Optional

from sqlalchemy import ForeignKey
from sqlalchemy import func
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import registry
from sqlalchemy.orm import relationship

mapper_registry = registry()


@mapper_registry.mapped
class User:
    __tablename__ = "user"

    id = mapped_column(Integer, primary_key=True)
    name: Mapped[str]
    fullname: Mapped[Optional[str]]
    nickname: Mapped[Optional[str]] = mapped_column(String(64))
    create_date: Mapped[datetime] = mapped_column(insert_default=func.now())

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


@mapper_registry.mapped
class Address:
    __tablename__ = "address"

    id = mapped_column(Integer, primary_key=True)
    user_id = mapped_column(ForeignKey("user.id"))
    email_address: Mapped[str]

    user: Mapped["User"] = relationship(back_populates="addresses")

使用上述风格时,特定类的映射将**仅**在装饰器直接应用于该类时进行。对于继承映射(在 映射类继承层次结构 中详细描述),应将装饰器应用于要映射的每个子类:

from sqlalchemy.orm import registry

mapper_registry = registry()


@mapper_registry.mapped
class Person:
    __tablename__ = "person"

    person_id = mapped_column(Integer, primary_key=True)
    type = mapped_column(String, nullable=False)

    __mapper_args__ = {
        "polymorphic_on": type,
        "polymorphic_identity": "person",
    }


@mapper_registry.mapped
class Employee(Person):
    __tablename__ = "employee"

    person_id = mapped_column(ForeignKey("person.person_id"), primary_key=True)

    __mapper_args__ = {
        "polymorphic_identity": "employee",
    }

声明式表配置风格 declarative table 和命令式表配置风格 imperative table 都可以与声明式基类或装饰器风格的声明式映射一起使用。

装饰器形式的映射在将SQLAlchemy声明式映射与其他类仪器系统(例如 dataclassesattrs )结合使用时很有用,但请注意,SQLAlchemy 2.0现在也具有与声明式基类的dataclasses集成。

As an alternative to using the “declarative base” class is to apply declarative mapping to a class explicitly, using either an imperative technique similar to that of a “classical” mapping, or more succinctly by using a decorator. The registry.mapped() function is a class decorator that can be applied to any Python class with no hierarchy in place. The Python class otherwise is configured in declarative style normally.

The example below sets up the identical mapping as seen in the previous section, using the registry.mapped() decorator rather than using the DeclarativeBase superclass:

from datetime import datetime
from typing import List
from typing import Optional

from sqlalchemy import ForeignKey
from sqlalchemy import func
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import registry
from sqlalchemy.orm import relationship

mapper_registry = registry()


@mapper_registry.mapped
class User:
    __tablename__ = "user"

    id = mapped_column(Integer, primary_key=True)
    name: Mapped[str]
    fullname: Mapped[Optional[str]]
    nickname: Mapped[Optional[str]] = mapped_column(String(64))
    create_date: Mapped[datetime] = mapped_column(insert_default=func.now())

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


@mapper_registry.mapped
class Address:
    __tablename__ = "address"

    id = mapped_column(Integer, primary_key=True)
    user_id = mapped_column(ForeignKey("user.id"))
    email_address: Mapped[str]

    user: Mapped["User"] = relationship(back_populates="addresses")

When using the above style, the mapping of a particular class will only proceed if the decorator is applied to that class directly. For inheritance mappings (described in detail at 映射类继承层次结构), the decorator should be applied to each subclass that is to be mapped:

from sqlalchemy.orm import registry

mapper_registry = registry()


@mapper_registry.mapped
class Person:
    __tablename__ = "person"

    person_id = mapped_column(Integer, primary_key=True)
    type = mapped_column(String, nullable=False)

    __mapper_args__ = {
        "polymorphic_on": type,
        "polymorphic_identity": "person",
    }


@mapper_registry.mapped
class Employee(Person):
    __tablename__ = "employee"

    person_id = mapped_column(ForeignKey("person.person_id"), primary_key=True)

    __mapper_args__ = {
        "polymorphic_identity": "employee",
    }

Both the declarative table and imperative table table configuration styles may be used with either the Declarative Base or decorator styles of Declarative mapping.

The decorator form of mapping is useful when combining a SQLAlchemy declarative mapping with other class instrumentation systems such as dataclasses and attrs, though note that SQLAlchemy 2.0 now features dataclasses integration with Declarative Base classes as well.