使用 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
CommonMixin
和 HasLogRecord
的基类,以及一个补充类 LogRecord
,其中还包括 CommonMixin
,演示了 mixin 和基类支持的各种结构,包括:
使用mapped_column()
声明的列、映射
或Column
从混音或基类复制到要映射的目标类;上面通过列属性CommonMixin.id
和HasLogRecord.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_attr
向
declared_attr
区分 Mapped
attributes 和 Declarative configurational attributes
mixin 和 base classes 的顺序没有固定的约定。普通的 Python 方法解析规则适用,上面的示例也适用于:
class MyModel(Base, HasLogRecord, CommonMixin):
name: Mapped[str] = mapped_column()
这之所以有效,是因为这里的 Base
没有定义任何
CommonMixin
或 HasLogRecord
定义,即 __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]
其中,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)
列混合¶
列可以在 mixins 中指示,假设
配置的声明性表样式
正在使用中(而不是
imperative table 配置),以便可以在 mixin 上声明的列被复制为 Declarative 进程生成的 Table
的一部分。所有三个 mapped_column()、
Mapped
和 Column
结构都可以在声明性 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
方法,消除了在复制关系及其可能的列绑定内容时可能出现的任何歧义。下面是一个示例,它结合了一个外键列和一个关系,以便两个类 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
)也可以在混合 classmethods 中使用,这些 classmethods 通常需要引用正在映射的类。对于需要引用本地映射列的方案,在通常情况下,这些列由 Declarative 作为映射类上的属性提供,该类作为 cls
参数传递给装饰的类方法。使用此功能,例如,我们可以使用
显式 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()
在被 mixin 使用时也需要生成类本地副本,因此也在由 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
装饰器使装饰后的可调用对象的行为与 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 指令的函数是
为层次结构中的每个子类调用,而生成映射属性的函数仅针对第一个映射的子类调用
层次结构中的超类。
这种行为差异的基本原理是基于这样一个事实,即映射属性已经可以被类继承,例如超类的映射表上的特定列也不应复制到子类的列,而特定于特定类或其映射表的元素是不可继承的。 例如本地映射的表的名称。
以下两节演示了这两个用例之间的行为差异。
使用继承 Table
和 Mapper
参数的 declared_attr()
¶
mixin 的一个常见方法是创建一个 def __tablename__(cls)
函数,该函数会动态地为映射的 Table
生成名称。
此配方可用于为继承的映射器层次结构生成表名,如以下示例所示,该示例创建一个 mixin,该 mixin 根据类名为每个类提供一个简单的表名。配方如下图所示,其中为 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
mixin 类的子类,将具有生成的__tablename__
属性,其中
Declarative 表示每个类都应该有自己的 Table
生成,它将被映射到该 GeneS Broker 中。 对于 Engineer
子类,应用的继承样式是 join table inheritance,因为它将映射到连接到基本 person 的
表 engineer
桌子。 默认情况下,从 Person
继承的任何其他子类也将应用这种继承样式(在这个特定示例中,每个子类都需要指定一个主键列;下一节将详细介绍)。
相比之下,Person
的 Manager
子类会覆盖
__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 上使用命名约定创建索引和约束¶
使用命名约束,如 Index
、UniqueConstraint
、
CheckConstraint
,其中每个对象对于从 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)
)