使用声明式的 Mapper 配置¶
映射类基本组件部分讨论了 Mapper
构造的一般配置元素,该构造定义了如何将特定用户定义的类映射到数据库表或其他 SQL 构造。以下部分描述了有关声明式系统如何构造 Mapper
的具体细节。
使用声明式定义映射属性¶
使用声明式的表配置中给出的示例
使用 mapped_column()
说明针对表绑定列的映射
构建。 还有其他几种 ORM 映射结构
,可以在 table bound columns 之外配置,最常见的是
relationship()
结构。其他类型的属性包括使用 column_property()
定义的 SQL 表达式
使用 composite()
构造和多列映射
构建。
虽然命令式映射利用 properties 字典来建立
所有映射的类属性,在声明性
mapping 中,这些属性都是在类定义中内联指定的,
在声明式表映射的情况下,它与
Column
对象,这些对象将用于生成
Table
对象。
使用 User
和 Address
的示例映射,我们可以说明一个声明式表映射,它不仅包括 mapped_column()
对象,还包括关系和 SQL 表达式:
from typing import List
from typing import Optional
from sqlalchemy import Column
from sqlalchemy import ForeignKey
from sqlalchemy import String
from sqlalchemy import Text
from sqlalchemy.orm import column_property
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[int] = mapped_column(primary_key=True)
name: Mapped[str]
firstname: Mapped[str] = mapped_column(String(50))
lastname: Mapped[str] = mapped_column(String(50))
fullname: Mapped[str] = column_property(firstname + " " + lastname)
addresses: Mapped[List["Address"]] = relationship(back_populates="user")
class Address(Base):
__tablename__ = "address"
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey("user.id"))
email_address: Mapped[str]
address_statistics: Mapped[Optional[str]] = mapped_column(Text, deferred=True)
user: Mapped["User"] = relationship(back_populates="addresses")
上面的声明式表映射包含两个表,每个表都有一个
relationship()
引用另一个,以及由 column_property()
映射的简单 SQL 表达式,以及一个额外的
mapped_column()
表示加载应基于 mapped_column.deferred
关键字定义的 “延迟” 基础。有关这些特定概念的更多文档,请参见 基本关系模式。
使用 column_property,并使用 Column Deferral 限制哪些列加载。
也可以使用 “hybrid table” 样式使用上述声明性映射来指定属性;直接属于表的 Column
对象将移动到 Table
定义中
但其他所有内容(包括组合的 SQL 表达式)仍将是
inline 的 INLINE 类定义。 需要引用
列
会直接引用它
Table
对象。为了使用混合表格样式说明上述映射:
# mapping attributes using declarative with imperative table
# i.e. __table__
from sqlalchemy import Column, ForeignKey, Integer, String, Table, Text
from sqlalchemy.orm import column_property
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import deferred
from sqlalchemy.orm import relationship
class Base(DeclarativeBase):
pass
class User(Base):
__table__ = Table(
"user",
Base.metadata,
Column("id", Integer, primary_key=True),
Column("name", String),
Column("firstname", String(50)),
Column("lastname", String(50)),
)
fullname = column_property(__table__.c.firstname + " " + __table__.c.lastname)
addresses = relationship("Address", back_populates="user")
class Address(Base):
__table__ = Table(
"address",
Base.metadata,
Column("id", Integer, primary_key=True),
Column("user_id", ForeignKey("user.id")),
Column("email_address", String),
Column("address_statistics", Text),
)
address_statistics = deferred(__table__.c.address_statistics)
user = relationship("User", back_populates="addresses")
上面需要注意的事项:
addressTable
包含一个名为address_statistics
的列,但是我们将此列重新映射到相同的属性名称下,以便在deferred()
结构的控制下。
对于声明式表和混合表映射,当我们定义ForeignKey
结构中,我们总是使用表名而不是映射的类名来命名目标表。
当我们定义relationship()
结构时,由于这些结构在两个映射类之间创建了链接,其中一个必须在另一个之前定义,因此我们可以使用其字符串名称来引用远程类。此功能还扩展到relationship()
上指定的其他参数区域,例如 “primary join” 和 “order by” 参数。有关详细信息,请参阅 Late-Evaluation of Relationship Arguments 部分。
具有声明式的 Mapper 配置选项¶
对于所有映射形式,类的映射都是通过成为 Mapper
对象一部分的参数来配置的。
最终接收这些参数的函数是
Mapper
函数,并从注册表
上定义的一个正面映射函数传递给它
对象。
对于映射的声明形式,使用 __mapper_args__
声明性类变量指定映射器参数,该变量是作为关键字参数传递给 Mapper
函数的字典。一些例子:
映射特定主键列
下面的示例说明了
Mapper.primary_key
参数,该参数将特定列建立为 ORM 应视为类主键的一部分,独立于模式级主键约束:
class GroupUsers(Base):
__tablename__ = "group_users"
user_id = mapped_column(String(40))
group_id = mapped_column(String(40))
__mapper_args__ = {"primary_key": [user_id, group_id]}
另请参阅
映射到一组显式主键列 - 显式列作为主键列的 ORM 映射的进一步背景知识
版本 ID 列
下面的示例说明了
Mapper.version_id_col
和
Mapper.version_id_generator
参数,用于配置
一个 ORM 维护的版本计数器,该计数器在
工作单元 冲洗过程:
from datetime import datetime
class Widget(Base):
__tablename__ = "widgets"
id = mapped_column(Integer, primary_key=True)
timestamp = mapped_column(DateTime, nullable=False)
__mapper_args__ = {
"version_id_col": timestamp,
"version_id_generator": lambda v: datetime.now(),
}
另请参阅
配置版本计数器 - ORM 版本计数器功能的背景
单个表继承
下面的示例说明了
Mapper.polymorphic_on
和
Mapper.polymorphic_identity
参数,用于配置单表继承映射:
class Person(Base):
__tablename__ = "person"
person_id = mapped_column(Integer, primary_key=True)
type = mapped_column(String, nullable=False)
__mapper_args__ = dict(
polymorphic_on=type,
polymorphic_identity="person",
)
class Employee(Person):
__mapper_args__ = dict(
polymorphic_identity="employee",
)
另请参阅
单表继承 - ORM 单表继承映射功能的背景。
动态构造 mapper 参数¶
__mapper_args__
字典可以从类绑定
descriptor 方法,而不是从固定字典中通过使用
declared_attr()
构造。这对于为映射器创建参数非常有用,这些参数以编程方式从 table 配置或 mapped 类的其他方面派生。动态__mapper_args__
属性通常在使用声明式 Mixin 或
abstract 基类。
例如,要从映射中省略任何具有特殊 Column.info
值的列,mixin 可以使用 __mapper_args__
方法从
cls.__table__
属性并将其传递给 Mapper.exclude_properties
收集:
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import select
from sqlalchemy import String
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import declared_attr
class ExcludeColsWFlag:
@declared_attr
def __mapper_args__(cls):
return {
"exclude_properties": [
column.key
for column in cls.__table__.c
if column.info.get("exclude", False)
]
}
class Base(DeclarativeBase):
pass
class SomeClass(ExcludeColsWFlag, Base):
__tablename__ = "some_table"
id = mapped_column(Integer, primary_key=True)
data = mapped_column(String)
not_needed = mapped_column(String, info={"exclude": True})
在上面,ExcludeColsWFlag
mixin 提供了每个类的 __mapper_args__
钩子,它将扫描包含键/值的 Column
对象
'exclude':将 True
传递给 Column.info
参数,然后将其字符串 “key” name 添加到Mapper.exclude_properties
集合,这将阻止生成的 Mapper
将这些列考虑用于任何 SQL作。
另请参阅
其他声明式映射指令¶
__declare_last__()
¶
__declare_last__()
钩子允许定义
一个类级函数,由
MapperEvents.after_configured()
事件,该事件在假定映射已完成且“配置”步骤完成后发生:
class MyClass(Base):
@classmethod
def __declare_last__(cls):
""" """
# do something with mappings
__declare_first__()
¶
与 __declare_last__()
类似,但在 mapper 配置开始时通过事件 MapperEvents.before_configured()
调用:
class MyClass(Base):
@classmethod
def __declare_first__(cls):
""" """
# do something before mappings are configured
元数据
¶
MetaData
集合通常用于为新的
Table
是与正在使用的注册表
对象关联的 registry.metadata
属性。当使用
声明性基类,例如
DeclarativeBase
超类以及遗留函数(如
declarative_base()
和 registry.generate_base()
中,此
MetaData
通常也以名为
.metadata
直接位于基类上,因此也通过继承位于映射类上。Declarative 使用此属性(如果存在)来确定目标 MetaData
集合,如果不存在,则使用直接与
注册表
。
此属性也可以分配给 for 以影响
MetaData
集合,用于单个基础和/或注册表
的每个映射层次结构。无论使用声明式基类还是直接使用 registry.mapped()
装饰器,这都会生效,从而允许下一节 __abstract__ 中的 metadata-per-abstract 基示例等模式。可以使用 registry.mapped()
来说明类似的模式,如下所示:
reg = registry()
class BaseOne:
metadata = MetaData()
class BaseTwo:
metadata = MetaData()
@reg.mapped
class ClassOne:
__tablename__ = "t1" # will use reg.metadata
id = mapped_column(Integer, primary_key=True)
@reg.mapped
class ClassTwo(BaseOne):
__tablename__ = "t1" # will use BaseOne.metadata
id = mapped_column(Integer, primary_key=True)
@reg.mapped
class ClassThree(BaseTwo):
__tablename__ = "t1" # will use BaseTwo.metadata
id = mapped_column(Integer, primary_key=True)
另请参阅
__abstract__
¶
__abstract__
会导致 Declarative 完全跳过该类的 table 或 mapper 的生成。可以像 mixin 一样在层次结构中添加类(参见 Mixin 和自定义基类),允许子类仅从特殊类扩展:
class SomeAbstractBase(Base):
__abstract__ = True
def some_helpful_method(self):
""" """
@declared_attr
def __mapper_args__(cls):
return {"helpful mapper arguments": True}
class MyMappedClass(SomeAbstractBase):
pass
__abstract__
的一种可能用途是使用
不同基础的元数据
:
class Base(DeclarativeBase):
pass
class DefaultBase(Base):
__abstract__ = True
metadata = MetaData()
class OtherBase(Base):
__abstract__ = True
metadata = MetaData()
在上面,从 DefaultBase
继承的类将使用一个
MetaData
作为表的注册表,以及那些继承自
OtherBase 将使用不同的 OtherBase
。然后,可以在不同的数据库中创建 table 本身:
DefaultBase.metadata.create_all(some_engine)
OtherBase.metadata.create_all(some_other_engine)
另请参阅
使用 polymorphic_abstract 构建更深的层次结构 - 适用于继承层次结构的“抽象”映射类的替代形式。
__table_cls__
¶
允许自定义用于生成 Table
的可调用对象 / 类。这是一个非常开放的钩子,可以允许对在此处生成的 Table
进行特殊自定义:
class MyMixin:
@classmethod
def __table_cls__(cls, name, metadata_obj, *arg, **kw):
return Table(f"my_{name}", metadata_obj, *arg, **kw)
上面的 mixin 将导致生成的所有 Table
对象都包含前缀 “my_”
,后跟通常使用
__tablename__
属性。
__table_cls__
还支持返回 None
的情况,这会导致该类被视为单表继承而不是其子类。这在某些自定义方案中可能很有用,以确定应该根据 table 本身的参数进行单表继承,例如,如果没有主键,则定义为 single-inheritance:
class AutoTable:
@declared_attr
def __tablename__(cls):
return cls.__name__
@classmethod
def __table_cls__(cls, *arg, **kw):
for obj in arg[1:]:
if (isinstance(obj, Column) and obj.primary_key) or isinstance(
obj, PrimaryKeyConstraint
):
return Table(*arg, **kw)
return None
class Person(AutoTable, Base):
id = mapped_column(Integer, primary_key=True)
class Employee(Person):
employee_name = mapped_column(String)
上面的 Employee
类将映射为针对 Person
的单表继承;employee_name
列将添加为 Person
表的成员。