具有声明性的映射器配置

优质
小牛编辑
130浏览
2023-12-01

断面 映射器配置概述 讨论了 Mapper construct,它是一种结构,用于定义如何将特定的用户定义类映射到数据库表或其他SQL构造。下面的部分描述了关于声明性系统如何构造 Mapper .

使用声明性定义映射属性

中给出的示例 具有声明性的表配置 说明针对表绑定列的映射;单个列到ORM类属性的映射在内部由 ColumnProperty 构造。还有许多其他类型的映射器属性,最常见的是 relationship() 构造。其他类型的属性包括使用 synonym() 构造,使用 column_property() 构造、延迟列和SQL表达式,这些表达式仅在访问时加载,使用 deferred() 构造。

imperative mapping 利用 properties 字典来建立所有映射的类属性,在声明性映射中,这些属性都与类定义内联指定,在声明性表映射的情况下,类定义内联 Column 将用于生成 Table 对象。

使用的示例映射 UserAddress ,我们可以说明一个声明性表映射,它不仅包括 Column 对象以及关系和SQL表达式:

# mapping attributes using declarative with declarative table
# i.e. __tablename__

from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy.orm import column_property, relationship, deferred
from sqlalchemy.orm import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'user'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    firstname = Column(String(50))
    lastname = Column(String(50))

    fullname = column_property(firstname + " " + lastname)

    addresses = relationship("Address", back_populates="user")

class Address(Base):
    __tablename__ = 'address'

    id = Column(Integer, primary_key=True)
    user_id = Column(ForeignKey("user.id"))
    email_address = Column(String)
    address_statistics = deferred(Column(Text))

    user = relationship("User", back_populates="addresses")

上面的声明性表映射有两个表,每个表都有一个 relationship() 引用另一个,以及由映射的简单SQL表达式 column_property() ,以及一个额外的 Column 将在“延迟”的基础上加载 deferred() 构造。有关这些特定概念的更多文档,请访问 基本关系模式使用列属性延迟列加载 .

属性也可以使用上面的“混合表”样式通过声明性映射来指定 Column 直接作为表的一部分的对象移动到 Table 但其他所有内容(包括组合的SQL表达式)仍将与类定义内联。需要引用 Column 直接引用 Table 对象。要使用混合表样式演示上述映射:

# mapping attributes using declarative with imperative table
# i.e. __table__

from sqlalchemy import Table
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy.orm import column_property, relationship, deferred
from sqlalchemy.orm import declarative_base

Base = declarative_base()

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")

以上注意事项:

  • 住址 Table 包含一个名为 address_statistics ,但是,我们在相同的属性名下重新映射此列,使其受 deferred() 构造。

  • 当我们定义一个 ForeignKey 构造时,我们总是使用 表名 ,而不是映射的类名。

  • 当我们定义 relationship() 构造,因为这些构造在两个映射类之间创建链接,其中一个必须在另一个之前定义,因此我们可以使用远程类的字符串名称来引用它。此功能还扩展到 relationship() 例如“primary join”和“order by”参数。参见章节 关系论据的后期评估 有关详细信息。

具有声明性的映射器配置选项

对于所有映射表单,类的映射是通过成为 Mapper 对象。最终接收这些参数的函数是 mapper() 函数中定义的前向映射函数之一传递给它 registry 对象。

对于声明形式的映射,使用 __mapper_args__ 作为声明性参数传递给字典的关键字 mapper() 功能。一些例子:

版本ID列

这个 mapper.version_id_colmapper.version_id_generator 参数::

from datetime import datetime

class Widget(Base):
    __tablename__ = 'widgets'

    id = Column(Integer, primary_key=True)
    timestamp = Column(DateTime, nullable=False)

    __mapper_args__ = {
        'version_id_col': timestamp,
        'version_id_generator': lambda v:datetime.now()
    }

单表继承

这个 mapper.polymorphic_onmapper.polymorphic_identity 参数::

class Person(Base):
    __tablename__ = 'person'

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

    __mapper_args__ = dict(
        polymorphic_on=type,
        polymorphic_identity="person"
    )

class Employee(Person):
    __mapper_args__ = dict(
        polymorphic_identity="employee"
    )

这个 __mapper_args__ 通过使用 declared_attr() 构造。部门 使用mixin组合映射层次 进一步讨论这个概念。

参见

使用mixin组合映射层次

其他声明性映射指令

__declare_last__()

这个 __declare_last__() 钩子允许定义类级函数,该函数由 MapperEvents.after_configured() 事件,在假定映射已完成且“配置”步骤已完成之后发生:

class MyClass(Base):
    @classmethod
    def __declare_last__(cls):
        ""
        # do something with mappings

__declare_first__()

喜欢 __declare_last__() ,但在映射器配置开始时通过 MapperEvents.before_configured() 事件:

class MyClass(Base):
    @classmethod
    def __declare_first__(cls):
        ""
        # do something before mappings are configured

0.9.3 新版功能.

metadata

这个 MetaData 集合,通常用于将新的 Table 是不是 registry.metadata 属性关联的 registry 正在使用的对象。当使用声明性基类(如由生成的基类)时 declarative_base() 以及 registry.generate_base() ,这个 MetaData 通常也作为名为 .metadata 它直接位于基类上,因此也通过继承位于映射类上。声明性使用此属性(如果存在)来确定目标 MetaData 集合,或者如果不存在,则使用 MetaData 直接与 registry

也可以向此属性赋值,以便影响 MetaData 集合将在每个映射层次结构的基础上用于单个基和/或 registry 。无论是使用声明性基类还是如果 registry.mapped() 直接使用修饰符,从而允许模式,如下一节中的每个抽象的元数据基本示例, __abstract__ 。类似的模式可以使用 registry.mapped() 详情如下:

reg = registry()

class BaseOne:
    metadata = MetaData()

class BaseTwo:
    metadata = MetaData()

@reg.mapped
class ClassOne:
    __tablename__ = 't1'  # will use reg.metadata

    id = Column(Integer, primary_key=True)

@reg.mapped
class ClassTwo(BaseOne):
    __tablename__ = 't1'  # will use BaseOne.metadata

    id = Column(Integer, primary_key=True)

@reg.mapped
class ClassThree(BaseTwo):
    __tablename__ = 't1'  # will use BaseTwo.metadata

    id = Column(Integer, primary_key=True)

在 1.4.3 版更改: 这个 registry.mapped() 修饰器将支持名为 .metadata 在班级上做替补 MetaData 集合,以代替 MetaData 那是在 registry 它本身。方法返回的基类的行为相匹配。 registry.generate_base()sqlalchemy.orm.declarative_base() 方法/函数。注意:此功能由于1.4.0、1.4.1和1.4.2中的回归而中断,即使在使用 declarative_base() ;需要1.4.3才能恢复行为。

参见

__abstract__

__abstract__

__abstract__ 使声明性完全跳过类的表或映射器的生成。类可以以与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):
    ""

一种可能的用途 __abstract__ 是使用一个 MetaData 对于不同的基础:

Base = declarative_base()

class DefaultBase(Base):
    __abstract__ = True
    metadata = MetaData()

class OtherBase(Base):
    __abstract__ = True
    metadata = MetaData()

上面,继承自 DefaultBase 将使用一个 MetaData 作为表的注册表,以及继承自 OtherBase 将使用另一个。然后可以在不同的数据库中创建表本身:

DefaultBase.metadata.create_all(some_engine)
OtherBase.metadata.create_all(some_other_engine)

__table_cls__

允许用于生成 Table 定制。这是一个非常开放的钩子,允许对 Table 那个在这里产生的:

class MyMixin(object):
    @classmethod
    def __table_cls__(cls, name, metadata_obj, *arg, **kw):
        return Table(
            "my_" + name,
            metadata_obj, *arg, **kw
        )

上面的混音会导致 Table 为包含前缀而生成的对象 "my_" ,后跟通常使用 __tablename__ 属性。

__table_cls__ 也支持返回的情况 None 这将导致类被视为单表继承而不是其子类。这在某些自定义方案中可能很有用,可以根据表本身的参数确定应该进行单表继承,例如,如果不存在主键,则将其定义为单继承::

class AutoTable(object):
    @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 = Column(Integer, primary_key=True)

class Employee(Person):
    employee_name = Column(String)

以上内容 Employee 类将映射为针对以下对象的单表继承 Personemployee_name 列将添加为 Person 桌子。