具有声明性的映射器配置
断面 映射器配置概述 讨论了 Mapper
construct,它是一种结构,用于定义如何将特定的用户定义类映射到数据库表或其他SQL构造。下面的部分描述了关于声明性系统如何构造 Mapper
.
使用声明性定义映射属性
中给出的示例 具有声明性的表配置 说明针对表绑定列的映射;单个列到ORM类属性的映射在内部由 ColumnProperty
构造。还有许多其他类型的映射器属性,最常见的是 relationship()
构造。其他类型的属性包括使用 synonym()
构造,使用 column_property()
构造、延迟列和SQL表达式,这些表达式仅在访问时加载,使用 deferred()
构造。
当 imperative mapping 利用 properties 字典来建立所有映射的类属性,在声明性映射中,这些属性都与类定义内联指定,在声明性表映射的情况下,类定义内联 Column
将用于生成 Table
对象。
使用的示例映射 User
和 Address
,我们可以说明一个声明性表映射,它不仅包括 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_col
和 mapper.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_on
和 mapper.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组合映射层次 进一步讨论这个概念。
参见
其他声明性映射指令
__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__
使声明性完全跳过类的表或映射器的生成。类可以以与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
类将映射为针对以下对象的单表继承 Person
; employee_name
列将添加为 Person
桌子。