声明性尽可能直观地支持所有三种继承形式。这个 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.id
和 engineers.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
将具有的所有属性 Person
和 Manager
但是 not 这个 primary_language
属性 Engineer
::
class Manager(Person):
__mapper_args__ = {'polymorphic_identity': 'manager'}
golf_swing = Column(String(50))
属性排除逻辑由 exclude_properties
映射器参数和声明性的默认行为可以通过传递显式 exclude_properties
集合(空的或其他的)到 __mapper_args__
.
注意上述 primary_language
和 golf_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
列在两者上声明 Engineer
和 Manager
将导致错误::
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}
帮助程序类 AbstractConcreteBase
和 ConcreteBase
为上述创建多态联合的系统提供自动化。有关详细信息,请参阅这些帮助程序的文档以及有关具体继承的主要ORM文档。