使用 Mixin 组合映射的层次结构


使用 Declarative 样式映射类时,一个常见的需求是在多个类之间共享通用功能,例如特定列、表或映射器选项、命名方案或其他映射属性。当使用声明式映射时,通过使用 mixin 类以及扩充声明式基类本身来支持这种习惯用法。


提示


除了 mixin 类之外,公共列选项也可以在使用 PEP 593Annotated 类型的许多类之间共享;看 将多个类型配置映射到 Python 类型,以及 将整列声明映射到 Python 类型,了解这些 SQLAlchemy 2.0 功能的背景信息。


以下是一些常见的混入习语的示例:

from sqlalchemy import ForeignKey
from sqlalchemy.orm import declared_attr
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 CommonMixin:
    """define a series of common elements that may be applied to mapped
    classes using this class as a mixin class."""

    @declared_attr.directive
    def __tablename__(cls) -> str:
        return cls.__name__.lower()

    __table_args__ = {"mysql_engine": "InnoDB"}
    __mapper_args__ = {"eager_defaults": True}

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


class HasLogRecord:
    """mark classes that have a many-to-one relationship to the
    ``LogRecord`` class."""

    log_record_id: Mapped[int] = mapped_column(ForeignKey("logrecord.id"))

    @declared_attr
    def log_record(self) -> Mapped["LogRecord"]:
        return relationship("LogRecord")


class LogRecord(CommonMixin, Base):
    log_info: Mapped[str]


class MyModel(CommonMixin, HasLogRecord, Base):
    name: Mapped[str]


上面的示例说明了一个包含两个 mixin 的类 MyModel CommonMixinHasLogRecord 的基类,以及一个补充类 LogRecord,其中还包括 CommonMixin,演示了 mixin 和基类支持的各种结构,包括:


  • 使用 mapped_column() 声明的列、映射Column 从混音或基类复制到要映射的目标类;上面通过列属性 CommonMixin.idHasLogRecord.log_record_id 来说明这一点。


  • 声明性指令,例如 __table_args____mapper_args__ 可以分配给 mixin 或 base 类,它们将在那里生效 自动用于从 mixin 或 base 继承的任何类。 上面的示例使用 __table_args____mapper_args__ 属性。


  • 所有声明性指令,包括所有 __tablename____table____table_args____mapper_args__,可以使用 用户定义的类方法,这些方法用 declared_attr装饰器(特别是 declared_attr.directive 子成员,稍后会详细介绍)。在上面,这是使用 def __tablename__(cls) 类方法动态生成 Table name 来说明的;当应用于 MyModel 类时,表名将生成为 “mymodel”,当应用于 LogRecord 类时,表名将生成为 “logrecord”。


  • 其他 ORM 属性(如 relationship())可以在目标类上生成,并使用用户定义的类方法进行映射,这些方法也使用 declared_attr 装饰器进行装饰。在上面,通过生成一个名为 LogRecord 的映射对象的多对一 relationship() 来说明这一点。


上述功能都可以使用 select() 进行演示 例:

>>> from sqlalchemy import select
>>> print(select(MyModel).join(MyModel.log_record))
SELECT mymodel.name, mymodel.id, mymodel.log_record_id FROM mymodel JOIN logrecord ON logrecord.id = mymodel.log_record_id


提示


declared_attr 的示例将尝试说明每个方法示例的正确 PEP 484 注释。 使用 annotation 和 declared_attr 函数是完全可选的,不被 Declare 使用;但是,这些 Comments 是必需的,以便传递 Mypy --strict 类型检查。


此外,declared_attr.directive 子成员 上图也是可选的,并且仅对 PEP 484 类型工具,因为它会根据预期的返回类型进行调整,当 创建方法来覆盖 Declarative 指令,例如 __tablename____mapper_args____table_args__


2.0 版本中的新功能: 作为 SQLAlchemy ORM 的 PEP 484 类型支持的一部分,declared_attrdeclared_attr区分 Mapped attributes 和 Declarative configurational attributes


mixin 和 base classes 的顺序没有固定的约定。普通的 Python 方法解析规则适用,上面的示例也适用于:

class MyModel(Base, HasLogRecord, CommonMixin):
    name: Mapped[str] = mapped_column()


这之所以有效,是因为这里的 Base 没有定义任何 CommonMixinHasLogRecord 定义,即 __tablename__ __table_args__id 等如果 Base 确实定义了同名的属性,则 inherits 列表中排在第一位的类将确定在新定义的类上使用哪个属性。


提示


虽然上面的示例使用 基于 Mapped 注解类的带注释的声明式表表单,mixin 类也可以很好地与非带注释和传统的声明式表单配合使用,例如直接使用 Column 而不是 mapped_column() 中。


在 2.0 版更改: 对于来自 1.4 系列 SQLAlchemy 的用户,他们可能一直在使用 mypy 插件, 假设 mypy 插件不再使用 declarative_mixin() 类装饰器来标记声明性 mixin。


扩充基础


除了使用纯 mixin 之外,此 section 也可以直接应用于基类,因为 应应用于从特定基派生的所有类。 示例 下面说明了上一节的一些示例,其中 类:

from sqlalchemy import ForeignKey
from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship


class Base(DeclarativeBase):
    """define a series of common elements that may be applied to mapped
    classes using this class as a base class."""

    @declared_attr.directive
    def __tablename__(cls) -> str:
        return cls.__name__.lower()

    __table_args__ = {"mysql_engine": "InnoDB"}
    __mapper_args__ = {"eager_defaults": True}

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


class HasLogRecord:
    """mark classes that have a many-to-one relationship to the
    ``LogRecord`` class."""

    log_record_id: Mapped[int] = mapped_column(ForeignKey("logrecord.id"))

    @declared_attr
    def log_record(self) -> Mapped["LogRecord"]:
        return relationship("LogRecord")


class LogRecord(Base):
    log_info: Mapped[str]


class MyModel(HasLogRecord, Base):
    name: Mapped[str]


其中,MyModelLogRecordBase,它们的表名都将派生自其类名、名为 id 的主键列,以及由 Base.__table_args__Base.__mapper_args__ 定义的上述表和映射器参数。


当使用旧版 declarative_base()registry.generate_base() 时,可以按如下方式使用 declarative_base.cls 参数来生成等效效果,如下面的未注释示例所示:

# legacy declarative_base() use

from sqlalchemy import Integer, String
from sqlalchemy import ForeignKey
from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship


class Base:
    """define a series of common elements that may be applied to mapped
    classes using this class as a base class."""

    @declared_attr.directive
    def __tablename__(cls):
        return cls.__name__.lower()

    __table_args__ = {"mysql_engine": "InnoDB"}
    __mapper_args__ = {"eager_defaults": True}

    id = mapped_column(Integer, primary_key=True)


Base = declarative_base(cls=Base)


class HasLogRecord:
    """mark classes that have a many-to-one relationship to the
    ``LogRecord`` class."""

    log_record_id = mapped_column(ForeignKey("logrecord.id"))

    @declared_attr
    def log_record(self):
        return relationship("LogRecord")


class LogRecord(Base):
    log_info = mapped_column(String)


class MyModel(HasLogRecord, Base):
    name = mapped_column(String)


列混合


列可以在 mixins 中指示,假设 配置的声明性表样式 正在使用中(而不是 imperative table 配置),以便可以在 mixin 上声明的列被复制为 Declarative 进程生成的 Table 的一部分。所有三个 mapped_column()、MappedColumn 结构都可以在声明性 mixin 中内联声明:

class TimestampMixin:
    created_at: Mapped[datetime] = mapped_column(default=func.now())
    updated_at: Mapped[datetime]


class MyModel(TimestampMixin, Base):
    __tablename__ = "test"

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


其中,所有包含 TimestampMixin 的声明性类 将自动包含一个列 created_at 将 Timestamp 应用于所有行插入,以及 updated_at 列,其中不包括示例的默认值 (如果是这样,我们将使用 mapped_column() 接受的 Column.onupdate 参数)。这些列构造始终从原始 mixin 或基类复制,以便相同的 mixin/base 类可以应用于任意数量的目标类,每个目标类都有自己的列构造。


mixin 支持所有 Declarative 列形式,包括:


  • 带注释的属性 - 存在或不存在 mapped_column():

    class TimestampMixin:
        created_at: Mapped[datetime] = mapped_column(default=func.now())
        updated_at: Mapped[datetime]

  • mapped_column - 存在或不存在 Maped

    class TimestampMixin:
        created_at = mapped_column(default=func.now())
        updated_at: Mapped[datetime] = mapped_column()

  • - 旧版声明式:

    class TimestampMixin:
        created_at = Column(DateTime, default=func.now())
        updated_at = Column(DateTime)


在上述每种形式中,Declarative 通过创建结构的副本来处理 mixin 类上基于列的属性,然后将其应用于目标类。


在 2.0 版更改: 声明式 API 现在可以容纳 Column 对象以及 mapped_column() 使用 mixins 时任何形式的结构,而无需使用 declared_attr() 中。以前阻止在 mixin 中直接使用带有 ForeignKey 元素的列的限制已被删除。


混合关系


提供了由 relationship() 创建的关系 对于声明式 mixin 类,仅使用 declared_attr方法,消除了在复制关系及其可能的列绑定内容时可能出现的任何歧义。下面是一个示例,它结合了一个外键列和一个关系,以便两个类 FooBar 都可以配置为通过多对一引用公共目标类:

from sqlalchemy import ForeignKey
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship


class Base(DeclarativeBase):
    pass


class RefTargetMixin:
    target_id: Mapped[int] = mapped_column(ForeignKey("target.id"))

    @declared_attr
    def target(cls) -> Mapped["Target"]:
        return relationship("Target")


class Foo(RefTargetMixin, Base):
    __tablename__ = "foo"
    id: Mapped[int] = mapped_column(primary_key=True)


class Bar(RefTargetMixin, Base):
    __tablename__ = "bar"
    id: Mapped[int] = mapped_column(primary_key=True)


class Target(Base):
    __tablename__ = "target"
    id: Mapped[int] = mapped_column(primary_key=True)


通过上述映射,FooBar 都包含与 Target 的关系,可通过 .target 属性访问:

>>> from sqlalchemy import select
>>> print(select(Foo).join(Foo.target))
SELECT foo.id, foo.target_id FROM foo JOIN target ON target.id = foo.target_id
>>> print(select(Bar).join(Bar.target))
SELECT bar.id, bar.target_id FROM bar JOIN target ON target.id = bar.target_id


特殊参数(如 relationship.primaryjoin)也可以在混合 classmethods 中使用,这些 classmethods 通常需要引用正在映射的类。对于需要引用本地映射列的方案,在通常情况下,这些列由 Declarative 作为映射类上的属性提供,该类作为 cls 参数传递给装饰的类方法。使用此功能,例如,我们可以使用 显式 primaryJoin,它引用两个 Targetcls

class Target(Base):
    __tablename__ = "target"
    id: Mapped[int] = mapped_column(primary_key=True)


class RefTargetMixin:
    target_id: Mapped[int] = mapped_column(ForeignKey("target.id"))

    @declared_attr
    def target(cls) -> Mapped["Target"]:
        # illustrates explicit 'primaryjoin' argument
        return relationship("Target", primaryjoin=Target.id == cls.target_id)


混合 column_property() 和其他 MapperProperty


relationship() 一样,其他 MapperProperty 子类,例如 column_property() 在被 mixin 使用时也需要生成类本地副本,因此也在由 declared_attr 装饰的函数中声明。在该函数中,使用 mapped_column() 声明的其他普通映射列, MappedColumn 将从 cls 参数中提供,以便它们可以用于组合新属性,如下面的示例所示,它将两列添加在一起:

from sqlalchemy.orm import column_property
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column


class Base(DeclarativeBase):
    pass


class SomethingMixin:
    x: Mapped[int]
    y: Mapped[int]

    @declared_attr
    def x_plus_y(cls) -> Mapped[int]:
        return column_property(cls.x + cls.y)


class Something(SomethingMixin, Base):
    __tablename__ = "something"

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


在上面,我们可以在生成完整表达式的语句中使用 Something.x_plus_y

>>> from sqlalchemy import select
>>> print(select(Something.x_plus_y))
SELECT something.x + something.y AS anon_1 FROM something


提示


declared_attr 装饰器使装饰后的可调用对象的行为与 classmethod 完全相同。但是,像 Pylance 这样的打字工具 可能无法识别这一点,这有时会导致它抱怨 关于对函数体内 CLS 变量的访问。为了在出现此问题时解决此问题,可以将 @classmethod 装饰器直接与 declared_attr 结合使用,如下所示:

class SomethingMixin:
    x: Mapped[int]
    y: Mapped[int]

    @declared_attr
    @classmethod
    def x_plus_y(cls) -> Mapped[int]:
        return column_property(cls.x + cls.y)


2.0 版本的新Function: - declared_attr 可以容纳一个用 @classmethod 修饰的函数来帮助 PEP 484 必要时集成。


使用带有映射继承模式的 Mixin 和基类


当处理 Mapper 继承模式时,如 映射类继承层次结构,当declared_attr与 mixin 类一起使用时,或者在类层次结构中同时扩充映射和未映射的超类时,会出现一些额外的功能。


在定义由 mixins 或基类上的 declared_attr 修饰的函数以由映射继承层次结构中的子类解释时,生成声明性使用的特殊名称(如 __tablename____mapper_args__的函数与可能生成普通映射属性(如 mapped_column() 和 )的函数之间存在重要区别 relationship() 的定义 Declarative 指令的函数是 为层次结构中的每个子类调用,而生成映射属性的函数仅针对第一个映射的子类调用 层次结构中的超类


这种行为差异的基本原理是基于这样一个事实,即映射属性已经可以被类继承,例如超类的映射表上的特定列也不应复制到子类的列,而特定于特定类或其映射表的元素是不可继承的。 例如本地映射的表的名称。


以下两节演示了这两个用例之间的行为差异。


使用继承 TableMapper 参数的 declared_attr()


mixin 的一个常见方法是创建一个 def __tablename__(cls) 函数,该函数会动态地为映射的 Table 生成名称。


此配方可用于为继承的映射器层次结构生成表名,如以下示例所示,该示例创建一个 mixin,该 mixin 根据类名为每个类提供一个简单的表名。配方如下图所示,其中为 Person 映射类和 PersonEngineer 子类生成表名,但不为 PersonManager 子类生成表名:

from typing import Optional

from sqlalchemy import ForeignKey
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column


class Base(DeclarativeBase):
    pass


class Tablename:
    @declared_attr.directive
    def __tablename__(cls) -> Optional[str]:
        return cls.__name__.lower()


class Person(Tablename, Base):
    id: Mapped[int] = mapped_column(primary_key=True)
    discriminator: Mapped[str]
    __mapper_args__ = {"polymorphic_on": "discriminator"}


class Engineer(Person):
    id: Mapped[int] = mapped_column(ForeignKey("person.id"), primary_key=True)

    primary_language: Mapped[str]

    __mapper_args__ = {"polymorphic_identity": "engineer"}


class Manager(Person):
    @declared_attr.directive
    def __tablename__(cls) -> Optional[str]:
        """override __tablename__ so that Manager is single-inheritance to Person"""

        return None

    __mapper_args__ = {"polymorphic_identity": "manager"}


在上面的示例中,Person 基类以及 Engineer 类是生成新表名的 Tablename mixin 类的子类,将具有生成的__tablename__ 属性,其中 Declarative 表示每个类都应该有自己的 Table 生成,它将被映射到该 GeneS Broker 中。 对于 Engineer 子类,应用的继承样式是 join table inheritance,因为它将映射到连接到基本 person 的engineer 桌子。 默认情况下,从 Person 继承的任何其他子类也将应用这种继承样式(在这个特定示例中,每个子类都需要指定一个主键列;下一节将详细介绍)。


相比之下,PersonManager 子类会覆盖 __tablename__ class方法返回 None。这向 Declarative 表明,此类不应生成 Table,而是专门使用 Person 被映射。对于 Manager 子类,应用的继承样式是单个表继承


上面的示例说明了像 __tablename__必须单独应用于每个子类,因为每个映射的类都需要说明它将映射到哪个 Table,或者它是否会将自身映射到继承的超类的 Table


如果我们想要反转上面说明的默认表方案,以便单个表继承是默认的,并且只有在提供 __tablename__ 指令来覆盖它时才能定义联接表继承,我们可以在最顶层的 __tablename__() 方法中使用声明式帮助程序,在本例中为名为 has_inherited_table() 的帮助程序。如果超类已经映射到 Table,则此函数将返回 True。我们可以在最基本的 __tablename__() 类方法中使用这个帮助程序,这样如果表已经存在,我们可以有条件地返回 None 作为表名,从而表示默认继承子类的单表继承:

from sqlalchemy import ForeignKey
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import has_inherited_table
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column


class Base(DeclarativeBase):
    pass


class Tablename:
    @declared_attr.directive
    def __tablename__(cls):
        if has_inherited_table(cls):
            return None
        return cls.__name__.lower()


class Person(Tablename, Base):
    id: Mapped[int] = mapped_column(primary_key=True)
    discriminator: Mapped[str]
    __mapper_args__ = {"polymorphic_on": "discriminator"}


class Engineer(Person):
    @declared_attr.directive
    def __tablename__(cls):
        """override __tablename__ so that Engineer is joined-inheritance to Person"""

        return cls.__name__.lower()

    id: Mapped[int] = mapped_column(ForeignKey("person.id"), primary_key=True)

    primary_language: Mapped[str]

    __mapper_args__ = {"polymorphic_identity": "engineer"}


class Manager(Person):
    __mapper_args__ = {"polymorphic_identity": "manager"}


使用 declared_attr() 生成特定于表的继承列


declared_attr一起使用时处理 __tablename__ 和其他特殊名称的方式相反,当我们混合列和属性(例如关系、列属性等)时,该函数仅在层次结构中为基类调用,除非 declared_attr 指令与 declared_attr.cascading 子指令。 下面,只有 Person 类将接收一个名为 id 的列;映射将在 Engineer 上失败,因为 Engineer 没有被赋予主键:

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


class Person(HasId, Base):
    __tablename__ = "person"

    discriminator: Mapped[str]
    __mapper_args__ = {"polymorphic_on": "discriminator"}


# this mapping will fail, as there's no primary key
class Engineer(Person):
    __tablename__ = "engineer"

    primary_language: Mapped[str]
    __mapper_args__ = {"polymorphic_identity": "engineer"}


在联接表继承中,通常情况是我们希望每个子类上都有不同命名的列。但是,在这种情况下,我们可能希望每个表上都有一个 id 列,并让它们通过 外键。 我们可以通过使用 declared_attr.cascading 修饰符,它表示应该为层次结构中的每个类调用该函数,几乎 (请参阅下面的警告)与 __tablename__ 相同:

class HasIdMixin:
    @declared_attr.cascading
    def id(cls) -> Mapped[int]:
        if has_inherited_table(cls):
            return mapped_column(ForeignKey("person.id"), primary_key=True)
        else:
            return mapped_column(Integer, primary_key=True)


class Person(HasIdMixin, Base):
    __tablename__ = "person"

    discriminator: Mapped[str]
    __mapper_args__ = {"polymorphic_on": "discriminator"}


class Engineer(Person):
    __tablename__ = "engineer"

    primary_language: Mapped[str]
    __mapper_args__ = {"polymorphic_identity": "engineer"}


警告


declared_attr.cascading 功能目前支持 不允许子类使用不同的函数或值覆盖属性。这是当前解决 @declared_attr 机制的限制,如果检测到此情况,则会发出警告。此限制仅适用于 ORM 映射列、关系和其他 MapperProperty 属性的样式。 它不适用于 __tablename____mapper_args__ 等声明性指令,这些指令 以与 declared_attr.cascading 中。


组合来自多个 Mixin 的 Table/Mapper 参数


__table_args____mapper_args__ 的情况下 指定,则可能需要合并 来自多个 mixin 的一些参数与你想要的那些 define 在类本身上定义。这 declared_attr 装饰器可以在此处用于创建从多个集合中提取的用户定义的排序规则例程:

from sqlalchemy.orm import declarative_mixin, declared_attr


class MySQLSettings:
    __table_args__ = {"mysql_engine": "InnoDB"}


class MyOtherMixin:
    __table_args__ = {"info": "foo"}


class MyModel(MySQLSettings, MyOtherMixin, Base):
    __tablename__ = "my_model"

    @declared_attr.directive
    def __table_args__(cls):
        args = dict()
        args.update(MySQLSettings.__table_args__)
        args.update(MyOtherMixin.__table_args__)
        return args

    id = mapped_column(Integer, primary_key=True)


在 Mixins 上使用命名约定创建索引和约束


使用命名约束,如 IndexUniqueConstraintCheckConstraint,其中每个对象对于从 mixin 派生的特定表都是唯一的,它要求为每个实际的映射类创建每个对象的单个实例。


举个简单的例子,要定义一个命名的、可能多列的索引 ,适用于从 Mixin 派生的所有表,请使用 索引并将其建立为 __table_args__ 的一部分,使用 declared_attr__table_args__() 建立为将为每个子类调用的类方法:

class MyMixin:
    a = mapped_column(Integer)
    b = mapped_column(Integer)

    @declared_attr.directive
    def __table_args__(cls):
        return (Index(f"test_idx_{cls.__tablename__}", "a", "b"),)


class MyModelA(MyMixin, Base):
    __tablename__ = "table_a"
    id = mapped_column(Integer, primary_key=True)


class MyModelB(MyMixin, Base):
    __tablename__ = "table_b"
    id = mapped_column(Integer, primary_key=True)


上面的示例将生成两个表 “table_a”“table_b”,索引为 “test_idx_table_a”“test_idx_table_b”


通常,在现代 SQLAlchemy 中,我们会使用命名约定,如 配置约束命名约定 中所述。虽然命名约定是使用 MetaData.naming_convention 创建新的 Constraint 对象时,因为此约定在对象构造时基于特定 Constraint 中,需要为每个继承的子类创建一个不同的 Constraint 对象,并拥有自己的 Table,再次使用 declared_attr __table_args__() ,下面使用抽象映射基进行说明:

from uuid import UUID

from sqlalchemy import CheckConstraint
from sqlalchemy import create_engine
from sqlalchemy import MetaData
from sqlalchemy import UniqueConstraint
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column

constraint_naming_conventions = {
    "ix": "ix_%(column_0_label)s",
    "uq": "uq_%(table_name)s_%(column_0_name)s",
    "ck": "ck_%(table_name)s_%(constraint_name)s",
    "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
    "pk": "pk_%(table_name)s",
}


class Base(DeclarativeBase):
    metadata = MetaData(naming_convention=constraint_naming_conventions)


class MyAbstractBase(Base):
    __abstract__ = True

    @declared_attr.directive
    def __table_args__(cls):
        return (
            UniqueConstraint("uuid"),
            CheckConstraint("x > 0 OR y < 100", name="xy_chk"),
        )

    id: Mapped[int] = mapped_column(primary_key=True)
    uuid: Mapped[UUID]
    x: Mapped[int]
    y: Mapped[int]


class ModelAlpha(MyAbstractBase):
    __tablename__ = "alpha"


class ModelBeta(MyAbstractBase):
    __tablename__ = "beta"


上面的映射将生成 DDL,其中包括所有约束的特定于表的名称,包括主键、CHECK 约束、唯一约束:

CREATE TABLE alpha (
    id INTEGER NOT NULL,
    uuid CHAR(32) NOT NULL,
    x INTEGER NOT NULL,
    y INTEGER NOT NULL,
    CONSTRAINT pk_alpha PRIMARY KEY (id),
    CONSTRAINT uq_alpha_uuid UNIQUE (uuid),
    CONSTRAINT ck_alpha_xy_chk CHECK (x > 0 OR y < 100)
)


CREATE TABLE beta (
    id INTEGER NOT NULL,
    uuid CHAR(32) NOT NULL,
    x INTEGER NOT NULL,
    y INTEGER NOT NULL,
    CONSTRAINT pk_beta PRIMARY KEY (id),
    CONSTRAINT uq_beta_uuid UNIQUE (uuid),
    CONSTRAINT ck_beta_xy_chk CHECK (x > 0 OR y < 100)
)