版本:1.1.0b2 |发布日期:2016年7月1日

SQLAlchemy 1.1文档

继承配置

声明支持尽可能直观的所有三种形式的继承。inherits映​​射关键字参数是不需要的,因为声明将从类本身确定。各种“多态”关键字参数是使用__mapper_args__指定的。

也可以看看

Mapping Class Inheritance Hierarchies - general introduction to inheritance mapping with Declarative.

连接表继承

联合表继承被定义为定义其自己的表的子类:

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

Note that above, the Engineer.id attribute, since it shares the same attribute name as the Person.id attribute, will in fact represent the people.id and engineers.id columns together, with the “Engineer.id” column taking precedence if queried directly. 要为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类在定义primary_language列之前映射到people,而这一列将不会被包含在自己的映射中。Engineer然后定义primary_language列时,该列被添加到people表中,以便它包含在Engineer也是表格全部列的一部分。未映射到Person的列也使用exclude_properties映射器参数从其他单个或已加入的继承类中排除。Below, Manager will have all the attributes of Person and Manager but not the primary_language attribute of 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)

以上,在EngineerManager上声明的start_date列都会导致错误:

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

在这样的情况下,Declarative不能确定意图,特别是如果start_date列有例如不同的类型。A situation like this can be resolved by using declared_attr to define the Column conditionally, taking care to return the existing column via the parent __table__ if it already exists:

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,它知道跳过重新分配同一列。If the mapping is mis-configured such that the start_date column is accidentally re-assigned to a different table (such as, if we changed Manager to be joined inheritance without fixing start_date), an error is raised which indicates an existing Column is trying to be re-assigned to a different owning Table.

New in version 0.8: declared_attr can be used on a non-mixin class, and the returned Column or other mapped attribute will be applied to the mapping as any other attribute. 以前,生成的属性将被忽略,并且在创建子类时也会发出警告。

New in version 0.8: declared_attr, when used either with a mixin or non-mixin declarative class, can return an existing Column already assigned to the parent Table, to indicate that the re-assignment of the Column should be skipped, however should still be mapped on the target class, in order to resolve duplicate column conflicts.

mixin类可以使用相同的概念(见Mixin and Custom Base Classes):

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'}

上面的mixin检查列的本地__table__属性。因为我们使用单表继承,所以我们确信在这种情况下,cls.__table__是指Person.__table__If we were mixing joined- and single-table inheritance, we might want our mixin to check more carefully if cls.__table__ is really the Table we’re looking for.

具体表继承

具体定义为具有自己的表的子类,并将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))

Usage of an abstract base class is a little less straightforward as it requires usage of polymorphic_union(), which needs to be created with the Table objects before the class is built:

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文档。