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

SQLAlchemy 1.1文档

继承配置

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

也可以看看

Mapping Class Inheritance Hierarchies - 使用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))

当配置上述映射器时,在定义primary_language列之前,将Person类映射到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))

The attribute exclusion logic is provided by the exclude_properties mapper argument, and declarative’s default behavior can be disabled by passing an explicit exclude_properties collection (empty or otherwise) to the __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类中。在这种情况下,Declarative让我们返回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.

0.8版中的新功能 declared_attr可用于非mixin类,并且返回的Column或其他映射属性将应用于映射为其他任何属性。以前,结果属性将被忽略,并且还会在创建子类时导致发出警告。

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__如果我们混合了连接表和单表继承,那么我们可能希望我们的mixin更仔细地检查是否我们正在查找cls.__table__真的是Table

具体表继承

混凝土定义为具有自己的表的子类,并将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文档。