SQLAlchemy 2.0 文档
- 前一章: 使用 Declarative 配置 Mapper
- 下一章: 与 dataclasses 和 attrs 集成
- 上一级: 首页
- 本页内容
使用 Mixin 构建映射层次结构¶
使用 Declarative 风格映射类时,一个常见需求是共享通用功能,例如特定列、表或 Mapper 选项、命名方案或其他映射属性,在许多类之间。当使用声明式映射时,此惯例通过使用 mixin 类 以及增强声明式基类本身来支持。
提示
除了 mixin 类之外,通用列选项也可以使用 PEP 593 Annotated
类型在许多类之间共享;有关这些 SQLAlchemy 2.0 功能的背景信息,请参阅 将多个类型配置映射到 Python 类型 和 将整个列声明映射到 Python 类型。
下面是一些常用混合惯例的示例
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]
上面的示例说明了一个类 MyModel
,它在基类中包含两个 mixin CommonMixin
和 HasLogRecord
,以及一个补充类 LogRecord
,它也包含 CommonMixin
,演示了 mixin 和基类上支持的各种结构,包括
使用
mapped_column()
、Mapped
或Column
声明的列将从 mixin 或基类复制到要映射的目标类;上面通过列属性CommonMixin.id
和HasLogRecord.log_record_id
说明了这一点。声明式指令,例如
__table_args__
和__mapper_args__
可以分配给 mixin 或基类,它们将自动生效,适用于继承自该 mixin 或基类的任何类。上面的示例使用__table_args__
和__mapper_args__
属性说明了这一点。所有 Declarative 指令,包括
__tablename__
、__table__
、__table_args__
和__mapper_args__
,都可以使用用户定义的类方法实现,这些方法使用declared_attr
装饰器装饰(具体来说是declared_attr.directive
子成员,稍后会详细介绍)。上面,这使用一个def __tablename__(cls)
类方法说明,该方法动态生成一个Table
名称;当应用于MyModel
类时,表名将生成为"mymodel"
,而当应用于LogRecord
类时,表名将生成为"logrecord"
。其他 ORM 属性,例如
relationship()
可以使用用户定义的类方法在要映射的目标类上生成,这些类方法也使用declared_attr
装饰器装饰。上面,这通过生成一个多对一relationship()
到一个名为LogRecord
的映射对象来说明。
上面列出的功能都可以使用一个 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 注释。在 declared_attr
函数中使用注释是完全可选的,并且不会被 Declarative 占用;但是,这些注释是通过 Mypy --strict
类型检查所必需的。
此外,上面说明的 declared_attr.directive
子成员也是可选的,它只对 PEP 484 类型工具有意义,因为它会在创建方法来覆盖声明式指令(如 __tablename__
、__mapper_args__
和 __table_args__
)时调整预期返回值类型。
新版功能: 作为 SQLAlchemy ORM 对 PEP 484 类型支持的一部分,在 declared_attr
中添加了 declared_attr.directive
,用于区分 Mapped
属性和声明式配置属性。
混入和基类的顺序没有固定的约定。普通的 Python 方法解析规则适用,上面的示例在使用以下顺序时也能正常工作。
class MyModel(Base, HasLogRecord, CommonMixin):
name: Mapped[str] = mapped_column()
这是因为这里的 Base
没有定义任何 CommonMixin
或 HasLogRecord
定义的变量,例如 __tablename__
、__table_args__
、id
等。如果 Base
定义了相同名称的属性,则继承列表中第一个出现的类将决定在新建类中使用哪个属性。
提示
虽然上面的示例使用的是基于 Mapped
注释类的 带注释的声明式表 形式,但混入类也能与非注释和遗留的声明式形式完美兼容,例如在使用 Column
而不是 mapped_column()
时。
更改内容: 对于来自 SQLAlchemy 1.4 系列的用户,他们可能一直在使用 mypy 插件,不再需要 declarative_mixin()
类装饰器来标记声明式混入,假设不再使用 mypy 插件。
扩展 Base¶
除了使用纯混入之外,本节中的大多数技术也可以直接应用于基类,用于应该应用于从特定基类派生的所有类的模式。下面的示例说明了上一节中的示例如何在 Base
类中实现。
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]
在上面,MyModel
和 LogRecord
都从 Base
派生,它们都将从其类名派生其表名,一个名为 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)
混入列¶
假设使用的是 声明式表 样式的配置(而不是 命令式表 配置),则可以在混入中指示列,这样声明在混入中的列就可以被复制,成为声明式过程生成的 Table
的一部分。三种 mapped_column()
、Mapped
和 Column
结构都可以在声明式混入中内联声明。
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
的列,该列会为所有行插入操作应用时间戳,以及一个名为 updated_at
的列,它不包含默认值(为了示例的需要,如果包含默认值,我们将使用 Column.onupdate
参数,该参数被 mapped_column()
接受)。这些列结构始终从原始的混入类或基类复制,这样相同的混入类/基类就可以应用于任意数量的目标类,而每个目标类都会有自己的列结构。
混入支持所有声明式列形式,包括:
带注释的属性 - 无论是否包含
mapped_column()
class TimestampMixin: created_at: Mapped[datetime] = mapped_column(default=func.now()) updated_at: Mapped[datetime]
mapped_column - 无论是否包含
Mapped
class TimestampMixin: created_at = mapped_column(default=func.now()) updated_at: Mapped[datetime] = mapped_column()
Column - 遗留的声明式形式
class TimestampMixin: created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime)
在以上每种形式中,声明式都会通过创建副本来处理混入类中的基于列的属性,然后将该副本应用于目标类。
更改内容: 声明式 API 现在可以容纳 Column
对象以及在使用混入时无需使用 declared_attr()
的任何形式的 mapped_column()
结构。之前阻止包含 ForeignKey
元素的列直接在混入中使用的问题已经解决。
混入关系¶
通过 relationship()
创建的关系专门使用 declared_attr
方法提供给声明式混入类,消除了在复制关系及其可能与列绑定的内容时可能出现的任何歧义。以下是一个示例,它组合了外键列和关系,以便两个类 Foo
和 Bar
都可以配置为通过多对一引用公共目标类。
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)
在上面的映射中,Foo
和 Bar
都包含对 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
之类的特殊参数也可以在混入的类方法中使用,这些方法通常需要引用正在映射的类。对于需要引用本地映射列的方案,在普通情况下,声明式会将这些列作为映射类上的属性提供,该映射类作为 cls
参数传递给装饰的类方法。使用此功能,例如,我们可以使用显式的 primaryjoin 重新编写 RefTargetMixin.target
方法,该 primaryjoin 引用 Target
和 cls
上待定的映射列。
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()
,也需要在混合类使用时生成类局部副本,因此也声明在由 declared_attr
装饰的函数内。在函数中,其他使用 mapped_column()
、Mapped
或 Column
声明的普通映射列将可从 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
装饰器使被装饰的可调用对象的行为完全像一个类方法。但是,像 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 中添加: - declared_attr
可以适应用 @classmethod
装饰的函数,以帮助 PEP 484 集成,在需要时。
在映射继承模式中使用混合类和基类¶
在处理映射继承模式时(如 映射类继承层次结构 中所述),在 declared_attr
与混合类一起使用或在类层次结构中增强映射和未映射的超类时,存在一些额外的功能。
在混合类或基类上定义由 declared_attr
装饰的函数,以便在映射继承层次结构中的子类中解释,在生成声明式使用的特殊名称(如 __tablename__
、__mapper_args__
)的函数与可能生成普通映射属性(如 mapped_column()
和 relationship()
)的函数之间存在重要区别。定义声明式指令的函数将对层次结构中的每个子类调用,而生成映射属性的函数将仅对层次结构中的第一个映射超类调用。
这种行为差异的理由是,映射属性已经是可继承的,例如超类映射表上的特定列不应该复制到子类上,而特定于特定类或其映射表的元素是不可继承的,例如本地映射的表的名称。
以下两节展示了这两种用例之间的行为差异。
使用 declared_attr()
继承 Table
和 Mapper
参数¶
混合类的一种常见方法是创建一个 def __tablename__(cls)
函数,该函数动态生成映射的 Table
的名称。
这种方法可用于为继承的映射器层次结构生成表名,如以下示例所示,该示例创建一个混合类,为每个类提供一个基于类名的简单表名。以下示例说明了该方法,该方法为 Person
映射类和 Person
的子类 Engineer
生成表名,但不会为 Person
的子类 Manager
生成表名。
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
混合类的子类)都将具有一个生成的 __tablename__
属性,这对声明式表示每个类都应该有自己的 Table
生成,该类将被映射到该表。对于 Engineer
子类,应用的继承样式是 连接表继承,因为它将被映射到一个表 engineer
,该表连接到基表 person
。从 Person
继承的任何其他子类也都将默认应用这种继承样式(在本示例中,每个子类都需要指定一个主键列;下一节将详细介绍)。
相比之下,Person
的子类 Manager
将覆盖 __tablename__
类方法以返回 None
。这表示声明式该类不应该生成一个 Table
,而是将专门使用映射到 Person
的基 Table
。对于 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
上失败,因为没有提供主键。
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 参数¶
对于使用声明性 Mixin 指定的 __table_args__
或 __mapper_args__
,您可能希望将来自多个 Mixin 的一些参数与您希望在类本身定义的参数组合在一起。可以使用 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)
在 Mixin 上使用命名约定创建索引和约束¶
使用命名约束,例如 Index
、UniqueConstraint
、CheckConstraint
,其中每个对象对于从 Mixin 派生的特定表都是唯一的,需要为每个实际映射的类创建一个每个对象的单独实例。
作为一个简单的例子,要定义一个命名的、可能包含多列的 Index
,该索引应用于从 Mixin 派生的所有表,请使用 Index
的“内联”形式,并将其作为 __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
的父 Table
的对象构造时应用的,因此需要为每个继承的子类创建一个单独的 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)
)
flambé! 龙和 炼金术士 图像设计由 Rotem Yaari 创建并慷慨捐赠。
使用 Sphinx 7.2.6 创建。文档最后生成时间:2024 年 11 月 8 日星期五上午 8:41:19 EST