继承配置¶

声明性尽可能直观地支持所有三种继承形式。这个 inherits 不需要mapper关键字参数,因为声明性将从类本身确定此参数。使用 __mapper_args__ .

参见

本节描述关于声明性系统如何与SQLAlchemy ORM继承配置交互的一些具体细节。见 映射类继承层次结构 对于继承映射的一般介绍。

联接表继承

联接表继承被定义为定义其自身表的子类::

class Person(Base):
    __tablename__ = 'people'
    id = Column(Integer, primary_key=True)
    discriminator = Column('type', String(50))
    __mapper_args__ = {'polymorphic_on': discriminator}

class Engineer(Person):
    __tablename__ = 'engineers'
    __mapper_args__ = {'polymorphic_identity': 'engineer'}
    id = Column(Integer, ForeignKey('people.id'), primary_key=True)
    primary_language = Column(String(50))

请注意,上面 Engineer.id 属性,因为它与 Person.id 属性,将实际上表示 people.idengineers.id 如果直接查询,列与“engineer.id”列一起优先。提供 Engineer 类的属性只表示 engineers.id 列,给它一个不同的属性名:

class Engineer(Person):
    __tablename__ = 'engineers'
    __mapper_args__ = {'polymorphic_identity': 'engineer'}
    engineer_id = Column('id', Integer, ForeignKey('people.id'),
                                                primary_key=True)
    primary_language = Column(String(50))

单表继承

单表继承被定义为一个子类,它没有自己的表;您只需去掉 __table____tablename__ 属性::

class Person(Base):
    __tablename__ = 'people'
    id = Column(Integer, primary_key=True)
    discriminator = Column('type', String(50))
    __mapper_args__ = {'polymorphic_on': discriminator}

class Engineer(Person):
    __mapper_args__ = {'polymorphic_identity': 'engineer'}
    primary_language = Column(String(50))

配置上述映射器时, Person 类映射到 people 表格 之前 这个 primary_language 列已定义,此列将不包含在其自己的映射中。什么时候? Engineer 然后定义 primary_language 列,该列将添加到 people 表,以便它包含在的映射中 Engineer 并且也是表的完整列集的一部分。未映射到的列 Person 也被排除在使用 exclude_properties 映射器参数。下面, Manager 将具有的所有属性 PersonManager 但是 not 这个 primary_language 属性 Engineer ::

class Manager(Person):
    __mapper_args__ = {'polymorphic_identity': 'manager'}
    golf_swing = Column(String(50))

属性排除逻辑由 exclude_properties 映射器参数和声明性的默认行为可以通过传递显式 exclude_properties 集合(空的或其他的)到 __mapper_args__ .

解决列冲突

注意上述 primary_languagegolf_swing 列被“上移”以应用于 Person.__table__ ,因为它们在没有自己表的子类上声明。当两个子类想要指定 相同的 列,如下所示:

class Person(Base):
    __tablename__ = 'people'
    id = Column(Integer, primary_key=True)
    discriminator = Column('type', String(50))
    __mapper_args__ = {'polymorphic_on': discriminator}

class Engineer(Person):
    __mapper_args__ = {'polymorphic_identity': 'engineer'}
    start_date = Column(DateTime)

class Manager(Person):
    __mapper_args__ = {'polymorphic_identity': 'manager'}
    start_date = Column(DateTime)

上面, start_date 列在两者上声明 EngineerManager 将导致错误::

sqlalchemy.exc.ArgumentError: Column 'start_date' on class
<class '__main__.Manager'> conflicts with existing
column 'people.start_date'

在这种情况下,声明性不能确定意图,特别是如果 start_date 例如,列有不同的类型。这样的情况可以通过使用 declared_attr 定义 Column 有条件地,注意归还 既有线 经由父母 __table__ 如果它已经存在:

from sqlalchemy.ext.declarative import declared_attr

class Person(Base):
    __tablename__ = 'people'
    id = Column(Integer, primary_key=True)
    discriminator = Column('type', String(50))
    __mapper_args__ = {'polymorphic_on': discriminator}

class Engineer(Person):
    __mapper_args__ = {'polymorphic_identity': 'engineer'}

    @declared_attr
    def start_date(cls):
        "Start date column, if not present already."
        return Person.__table__.c.get('start_date', Column(DateTime))

class Manager(Person):
    __mapper_args__ = {'polymorphic_identity': 'manager'}

    @declared_attr
    def start_date(cls):
        "Start date column, if not present already."
        return Person.__table__.c.get('start_date', Column(DateTime))

以上,何时 Manager 映射到 start_date 列已存在于 Person 班级。声明性让我们返回 Column 因此,在这种情况下,它知道跳过重新分配同一列。如果映射配置错误,导致 start_date 列被意外地重新分配到另一个表(例如,如果我们更改了 Manager 不加固定地加入继承 start_date )将引发一个错误,指示存在 Column 正在尝试重新分配给其他所有者 Table .

相同的概念可用于mixin类(请参见 混合和自定义基类 ):

class Person(Base):
    __tablename__ = 'people'
    id = Column(Integer, primary_key=True)
    discriminator = Column('type', String(50))
    __mapper_args__ = {'polymorphic_on': discriminator}

class HasStartDate(object):
    @declared_attr
    def start_date(cls):
        return cls.__table__.c.get('start_date', Column(DateTime))

class Engineer(HasStartDate, Person):
    __mapper_args__ = {'polymorphic_identity': 'engineer'}

class Manager(HasStartDate, Person):
    __mapper_args__ = {'polymorphic_identity': 'manager'}

上面的混合检查本地 __table__ 列的属性。因为我们使用单表继承,所以我们确信在这种情况下, cls.__table__Person.__table__ . 如果我们混合联接表和单表继承,我们可能希望我们的mixin更仔细地检查 cls.__table__ 真的是 Table 我们在找。

具体的表继承

concrete被定义为一个子类,它有自己的表并设置 concrete 关键字参数 True ::

class Person(Base):
    __tablename__ = 'people'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))

class Engineer(Person):
    __tablename__ = 'engineers'
    __mapper_args__ = {'concrete':True}
    id = Column(Integer, primary_key=True)
    primary_language = Column(String(50))
    name = Column(String(50))

抽象基类的使用不那么简单,因为它需要使用 polymorphic_union() ,需要使用 Table 生成类之前的对象:

engineers = Table('engineers', Base.metadata,
                Column('id', Integer, primary_key=True),
                Column('name', String(50)),
                Column('primary_language', String(50))
            )
managers = Table('managers', Base.metadata,
                Column('id', Integer, primary_key=True),
                Column('name', String(50)),
                Column('golf_swing', String(50))
            )

punion = polymorphic_union({
    'engineer':engineers,
    'manager':managers
}, 'type', 'punion')

class Person(Base):
    __table__ = punion
    __mapper_args__ = {'polymorphic_on':punion.c.type}

class Engineer(Person):
    __table__ = engineers
    __mapper_args__ = {'polymorphic_identity':'engineer', 'concrete':True}

class Manager(Person):
    __table__ = managers
    __mapper_args__ = {'polymorphic_identity':'manager', 'concrete':True}

帮助程序类 AbstractConcreteBaseConcreteBase 为上述创建多态联合的系统提供自动化。有关详细信息,请参阅这些帮助程序的文档以及有关具体继承的主要ORM文档。

参见

具体的表继承

inheritance_concrete_helpers