SQLAlchemy支持三种继承形式: 单表继承 ,其中几种类型的类由一个表表示, 具体的表继承 ,其中每种类型的类都由独立的表表示,并且 联接表继承 ,其中类层次结构在依赖表中被分解,每个类都由其自己的表表示,该表只包含该类的本地属性。
最常见的继承形式是单表和联接表,而具体的继承则面临更多的配置挑战。
在继承关系中配置映射器时,SQLAlchemy可以加载元素 polymorphically ,表示单个查询可以返回多个类型的对象。
参见
继承映射配方 -连接、单一和具体继承的完整示例
在联合表继承中,沿着类层次结构的每个类都由一个不同的表表示。查询层次结构中的特定子类将沿其继承路径中的所有表呈现为SQL联接。如果查询的类是基类,则 默认行为是只包括基表 在select语句中。在所有情况下,为给定行实例化的最终类都由鉴别器列或对基表有效的表达式确定。加载子类时 only 对于基表,结果对象将首先填充基属性;子类的本地属性将 lazy load 当它们被访问时。另外,还有一些选项可以更改默认行为,允许查询前面包含与多个表/子类对应的列。
联合继承层次结构中的基类配置了其他参数,这些参数将引用多态鉴别器列以及基类的标识符:
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
type = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'employee',
'polymorphic_on':type
}
上面,一个附加的列 type
被确定为 鉴别器 ,使用 mapper.polymorphic_on
参数。此列将存储一个值,该值指示行中表示的对象类型。列可以是任何数据类型,但字符串和整数是最常见的。要应用于数据库中特定行的此列的实际数据值是使用 mapper.polymorphic_identity
参数,如下所述。
虽然多态性鉴别器表达不是严格必要的,但如果需要多态性加载,则需要它。在基表上建立一个简单的列是实现这一点的最简单方法,但是非常复杂的继承映射甚至可以将SQL表达式(如case语句)配置为多态鉴别器。
注解
目前, 只能为整个继承层次结构配置一个鉴别器列或SQL表达式 ,通常位于层次结构中最基本的类上。“尚不支持级联的“多态鉴别器表达式”。
我们下一个定义 Engineer
和 Manager
亚类 Employee
. 每个列都包含表示它们所表示的子类唯一属性的列。每个表还必须包含主键列(或列),以及对父表的外键引用::
class Engineer(Employee):
__tablename__ = 'engineer'
id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
engineer_name = Column(String(30))
__mapper_args__ = {
'polymorphic_identity':'engineer',
}
class Manager(Employee):
__tablename__ = 'manager'
id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
manager_name = Column(String(30))
__mapper_args__ = {
'polymorphic_identity':'manager',
}
在上面的示例中,每个映射都指定 mapper.polymorphic_identity
参数在其映射器参数中。此值填充由指定的列 mapper.polymorphic_on
在基础映射器上建立的参数。这个 mapper.polymorphic_identity
参数对于整个层次结构中的每个映射类都应该是唯一的,并且每个映射类只能有一个“标识”;如上所述,不支持某些子类引入第二个标识的“级联”标识。
ORM使用由设置的值 mapper.polymorphic_identity
以便在多态加载行时确定行属于哪个类。在上面的示例中,表示 Employee
会有价值的 'employee'
在其 type
同样,每行;每行 Engineer
会得到价值 'engineer'
,每一个 Manager
会得到价值 'manager'
. 不管继承映射是在联接表继承中为子类使用不同的联接表,还是像在单表继承中那样使用全部一个表,这个值都应该在查询时被持久化并可供ORM使用。这个 mapper.polymorphic_identity
参数也适用于具体的表继承,但实际上并不是持久化的;请参见 具体的表继承 有关详细信息。
在多态设置中,最常见的情况是在与主键本身相同的一列或多个列上建立外键约束,但这不是必需的;也可以使与主键不同的列通过外键引用父项。从基表到子类的连接构造方式也可以直接自定义,但这很少是必需的。
联接的继承主键
联合表继承配置的一个自然效果是,任何映射对象的标识都可以完全由基表中的行来确定。这有明显的优点,因此sqlAlchemy总是将联接继承类的主键列视为仅基表的主键列。也就是说, id
两列 engineer
和 manager
表不用于定位 Engineer
或 Manager
对象-仅限中的值 employee.id
被认为是。 engineer.id
和 manager.id
当然,在语句中确定父行后,它们仍然对模式整体的正确操作至关重要,因为它们用于定位联接行。
连接继承映射完成后,查询 Employee
将返回 Employee
, Engineer
和 Manager
物体。新保存的 Engineer
, Manager
和 Employee
对象将自动填充 employee.type
在这种情况下具有正确“discriminator”值的列 "engineer"
, "manager"
或 "employee"
,视情况而定。
联接表继承完全支持关系。涉及已联接继承类的关系应以层次结构中也对应于外键约束的类为目标;在下面,作为 employee
表具有返回到的外键约束 company
表中,在 Company
和 Employee
::
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String(50))
employees = relationship("Employee", back_populates="company")
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
type = Column(String(50))
company_id = Column(ForeignKey('company.id'))
company = relationship("Company", back_populates="employees")
__mapper_args__ = {
'polymorphic_identity':'employee',
'polymorphic_on':type
}
class Manager(Employee):
# ...
class Engineer(Employee):
# ...
如果外键约束位于对应于子类的表上,那么关系应该以该子类为目标。在下面的示例中,有一个外键约束来自 manager
到 company
,因此在 Manager
和 Company
班级:
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String(50))
managers = relationship("Manager", back_populates="company")
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
type = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'employee',
'polymorphic_on':type
}
class Manager(Employee):
__tablename__ = 'manager'
id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
manager_name = Column(String(30))
company_id = Column(ForeignKey('company.id'))
company = relationship("Company", back_populates="managers")
__mapper_args__ = {
'polymorphic_identity':'manager',
}
class Engineer(Employee):
# ...
上面, Manager
班级将有一个 Manager.company
属性; Company
将有一个 Company.managers
总是针对 employee
和 manager
桌子放在一起。
参见章节 正在加载继承层次结构 和 正在加载具有联接表继承的对象 对于继承加载技术的背景,包括要在映射器配置时和查询时查询的表的配置。
单表继承表示单表中所有子类的所有属性。具有该类独有属性的特定子类将在表中的列中持久化这些属性,否则,如果该行引用不同类型的对象,这些列将为空。
查询层次结构中的特定子类将呈现为对基表的选择,该基表将包含一个WHERE子句,该子句将行限制为那些在鉴别器列或表达式中具有特定值的行。
与联接表继承相比,单表继承具有简单的优点;查询效率更高,因为只有一个表需要参与,才能加载每个表示类的对象。
单表继承配置与联接表继承非常相似,只是基类指定了 __tablename__
. 基表上还需要一个鉴别器列,这样类就可以彼此区分。
即使子类共享其所有属性的基表,在使用声明性时, Column
对象仍然可以在子类上指定,这表示该列只映射到该子类; Column
将应用于同一基础 Table
对象:
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
type = Column(String(20))
__mapper_args__ = {
'polymorphic_on':type,
'polymorphic_identity':'employee'
}
class Manager(Employee):
manager_data = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'manager'
}
class Engineer(Employee):
engineer_info = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'engineer'
}
注意,派生类管理器和工程师的映射器省略了 __tablename__
,指示它们没有自己的映射表。
请注意,在上一节中 manager_name
和 engineer_info
列被“上移”以应用于 Employee.__table__
,因为它们在没有自己表的子类上声明。当两个子类想要指定 相同的 列,如下所示:
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
type = Column(String(20))
__mapper_args__ = {
'polymorphic_on':type,
'polymorphic_identity':'employee'
}
class Engineer(Employee):
__mapper_args__ = {'polymorphic_identity': 'engineer'}
start_date = Column(DateTime)
class Manager(Employee):
__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 'employee.start_date'
上面的场景为声明性映射系统提供了一个模糊性,可以通过使用 declared_attr
定义 Column
有条件地,注意归还 既有线 经由父母 __table__
如果它已经存在:
from sqlalchemy.orm import declared_attr
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
type = Column(String(20))
__mapper_args__ = {
'polymorphic_on':type,
'polymorphic_identity':'employee'
}
class Engineer(Employee):
__mapper_args__ = {'polymorphic_identity': 'engineer'}
@declared_attr
def start_date(cls):
"Start date column, if not present already."
return Employee.__table__.c.get('start_date', Column(DateTime))
class Manager(Employee):
__mapper_args__ = {'polymorphic_identity': 'manager'}
@declared_attr
def start_date(cls):
"Start date column, if not present already."
return Employee.__table__.c.get('start_date', Column(DateTime))
以上,何时 Manager
映射到 start_date
列已存在于 Employee
类;通过返回现有的 Column
对象时,声明性系统识别出这是要分别映射到两个不同子类的同一列。
mixin类也可以使用类似的概念(请参见 使用mixin组合映射层次 )要从可重用mixin类定义一系列特定的列和/或其他映射属性:
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
type = Column(String(20))
__mapper_args__ = {
'polymorphic_on':type,
'polymorphic_identity':'employee'
}
class HasStartDate:
@declared_attr
def start_date(cls):
return cls.__table__.c.get('start_date', Column(DateTime))
class Engineer(HasStartDate, Employee):
__mapper_args__ = {'polymorphic_identity': 'engineer'}
class Manager(HasStartDate, Employee):
__mapper_args__ = {'polymorphic_identity': 'manager'}
单表继承完全支持关系。配置的方式与联接继承的方式相同;外键属性应位于关系的“外部”端的同一类上:
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String(50))
employees = relationship("Employee", back_populates="company")
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
type = Column(String(50))
company_id = Column(ForeignKey('company.id'))
company = relationship("Company", back_populates="employees")
__mapper_args__ = {
'polymorphic_identity':'employee',
'polymorphic_on':type
}
class Manager(Employee):
manager_data = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'manager'
}
class Engineer(Employee):
engineer_info = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'engineer'
}
此外,与联合继承的情况一样,我们可以创建涉及特定子类的关系。查询时,select语句将包含一个WHERE子句,该子句将类选择限制为该子类或子类:
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String(50))
managers = relationship("Manager", back_populates="company")
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
type = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'employee',
'polymorphic_on':type
}
class Manager(Employee):
manager_name = Column(String(30))
company_id = Column(ForeignKey('company.id'))
company = relationship("Company", back_populates="managers")
__mapper_args__ = {
'polymorphic_identity':'manager',
}
class Engineer(Employee):
engineer_info = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'engineer'
}
上面, Manager
班级将有一个 Manager.company
属性; Company
将有一个 Company.managers
总是针对 employee
使用附加的WHERE子句将行限制为 type = 'manager'
.
单表继承的加载技术与用于连接表继承的加载技术基本相同,并且这两种映射类型之间提供了高度的抽象性,以便在它们之间切换以及在单个层次结构中混合它们(只需省略 __tablename__
从属于单一继承的子类)。参见章节 正在加载继承层次结构 和 加载具有单表继承的对象 有关继承加载技术的文档,包括要在映射器配置时和查询时查询的类的配置。
具体继承将每个子类映射到它自己的不同表,每个表包含生成该类实例所需的所有列。默认情况下,具体的继承配置以非多态方式查询;对特定类的查询将只查询该类的表,并且只返回该类的实例。具体类的多态加载是通过在映射器中配置一个特殊的选择来实现的,该选择通常作为所有表的联合产生。
警告
具体的表继承是 更复杂的是 而不是联合的或单表继承,并且 功能更加有限 特别是关于将其用于关系、渴望加载和多态加载。当使用多态性时,它产生 非常大的查询 使用的联合不如简单的联接效果好。强烈建议,如果需要关系加载和多态加载的灵活性,则尽可能使用联接表或单表继承。如果不需要多态加载,那么如果每个类完全引用自己的表,就可以使用普通的非继承映射。
尽管连接表继承和单表继承在“多态”加载方面非常流利,但在具体继承中,这是一个更为棘手的问题。因此,当 不需要多态加载 . 建立涉及具体继承类的关系也更加困难。
要使用具体继承建立类,请添加 mapper.concrete
中的参数 __mapper_args__
. 这向声明性和映射指示不应将超类表视为映射的一部分:
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
class Manager(Employee):
__tablename__ = 'manager'
id = Column(Integer, primary_key=True)
name = Column(String(50))
manager_data = Column(String(50))
__mapper_args__ = {
'concrete': True
}
class Engineer(Employee):
__tablename__ = 'engineer'
id = Column(Integer, primary_key=True)
name = Column(String(50))
engineer_info = Column(String(50))
__mapper_args__ = {
'concrete': True
}
应注意两个关键点:
我们必须 显式定义所有列 在每个子类上,甚至是相同名称的子类上。一列,如 Employee.name
这里是 not 复制到由映射的表 Manager
或 Engineer
对我们来说。
而 Engineer
和 Manager
类在继承关系中与映射 Employee
他们仍然 不包括多态加载 . 意思是,如果我们查询 Employee
对象 manager
和 engineer
根本不查询表。
具有具体继承的多态加载要求针对每个应该具有多态加载的基类配置专门的select。此select需要能够单独访问所有映射表,并且通常是使用sqlAlchemy助手构造的union语句。 polymorphic_union()
.
正如在 正在加载继承层次结构 ,可以将任何类型的映射器继承配置配置为默认情况下使用 mapper.with_polymorphic
争论。当前公共API要求在 Mapper
第一次施工时。
但是,在声明性的情况下,映射器和 Table
一旦定义了映射类,就会立即创建映射类。这意味着 mapper.with_polymorphic
还无法提供参数,因为 Table
与子类相对应的对象尚未定义。
有几个策略可以解决这个循环,但是声明性提供了助手类。 ConcreteBase
和 AbstractConcreteBase
在幕后处理这个问题。
使用 ConcreteBase
我们可以用与其他形式的继承映射几乎相同的方式设置具体映射:
from sqlalchemy.ext.declarative import ConcreteBase
class Employee(ConcreteBase, Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
__mapper_args__ = {
'polymorphic_identity': 'employee',
'concrete': True
}
class Manager(Employee):
__tablename__ = 'manager'
id = Column(Integer, primary_key=True)
name = Column(String(50))
manager_data = Column(String(40))
__mapper_args__ = {
'polymorphic_identity': 'manager',
'concrete': True
}
class Engineer(Employee):
__tablename__ = 'engineer'
id = Column(Integer, primary_key=True)
name = Column(String(50))
engineer_info = Column(String(40))
__mapper_args__ = {
'polymorphic_identity': 'engineer',
'concrete': True
}
上面,声明性设置了 Employee
在映射器“初始化”时初始化;这是用于解析其他依赖映射器的映射器的最新配置步骤。这个 ConcreteBase
助手使用 polymorphic_union()
函数在设置所有其他类之后创建所有具体映射表的联合,然后使用现有的基类映射器配置此语句。
选择后,多态联合生成如下查询:
session.query(Employee).all()
SELECT
pjoin.id AS pjoin_id,
pjoin.name AS pjoin_name,
pjoin.type AS pjoin_type,
pjoin.manager_data AS pjoin_manager_data,
pjoin.engineer_info AS pjoin_engineer_info
FROM (
SELECT
employee.id AS id,
employee.name AS name,
CAST(NULL AS VARCHAR(50)) AS manager_data,
CAST(NULL AS VARCHAR(50)) AS engineer_info,
'employee' AS type
FROM employee
UNION ALL
SELECT
manager.id AS id,
manager.name AS name,
manager.manager_data AS manager_data,
CAST(NULL AS VARCHAR(50)) AS engineer_info,
'manager' AS type
FROM manager
UNION ALL
SELECT
engineer.id AS id,
engineer.name AS name,
CAST(NULL AS VARCHAR(50)) AS manager_data,
engineer.engineer_info AS engineer_info,
'engineer' AS type
FROM engineer
) AS pjoin
上面的联合查询需要为每个子表生成“空”列,以便容纳那些不是该特定子类成员的列。
到目前为止,所示的具体映射既显示了子类,也显示了映射到各个表的基类。在具体的继承用例中,通常不在数据库中表示基类,只表示子类。换句话说,基类是“抽象的”。
通常,当您希望将两个不同的子类映射到单个表,并且不映射基类时,这很容易实现。使用声明性时,只需使用 __abstract__
指标:
class Employee(Base):
__abstract__ = True
class Manager(Employee):
__tablename__ = 'manager'
id = Column(Integer, primary_key=True)
name = Column(String(50))
manager_data = Column(String(40))
__mapper_args__ = {
'polymorphic_identity': 'manager',
}
class Engineer(Employee):
__tablename__ = 'engineer'
id = Column(Integer, primary_key=True)
name = Column(String(50))
engineer_info = Column(String(40))
__mapper_args__ = {
'polymorphic_identity': 'engineer',
}
上面,我们实际上并没有使用sqlachemy的继承映射工具;我们可以加载和持久化 Manager
和 Engineer
通常情况下。但是当我们需要的时候情况会改变 多态查询 也就是说,我们想发射 session.query(Employee)
并取回一系列 Manager
和 Engineer
实例。这使我们回到具体继承领域,我们必须针对 Employee
为了达到这个目的。
映射器始终可以选择
在sqlAlchemy中,类的映射器总是需要引用一些“可选的”,通常是 Table
但也可以指 select()
对象也一样。虽然看起来“单表继承”映射器没有映射到表,但实际上,这些映射器隐式地引用了由超类映射的表。
为了修改我们的具体继承示例以说明能够进行多态加载的“抽象”基,我们将只有一个 engineer
和A manager
表与否 employee
然而,表 Employee
映射器将直接映射到“多态联合”,而不是在本地将其指定到 mapper.with_polymorphic
参数。
为了帮助实现这一点,声明性提供了 ConcreteBase
类称为 AbstractConcreteBase
自动实现:
from sqlalchemy.ext.declarative import AbstractConcreteBase
class Employee(AbstractConcreteBase, Base):
pass
class Manager(Employee):
__tablename__ = 'manager'
id = Column(Integer, primary_key=True)
name = Column(String(50))
manager_data = Column(String(40))
__mapper_args__ = {
'polymorphic_identity': 'manager',
'concrete': True
}
class Engineer(Employee):
__tablename__ = 'engineer'
id = Column(Integer, primary_key=True)
name = Column(String(50))
engineer_info = Column(String(40))
__mapper_args__ = {
'polymorphic_identity': 'engineer',
'concrete': True
}
这个 AbstractConcreteBase
helper类的内部进程比 ConcreteBase
,其中必须延迟基类的整个映射,直到所有子类都已声明。对于上面这样的映射,只有 Manager
和 Engineer
可能被持久化;查询 Employee
类将始终生成 Manager
和 Engineer
物体。
说明的声明性配置 ConcreteBase
和 AbstractConcreteBase
相当于使用 polymorphic_union()
明确地。这些构型利用 Table
对象,以便首先创建“多态联合”,然后将其应用于映射。这里对这些进行了说明,以阐明 polymorphic_union()
在映射方面的函数。
A semi-classical mapping 例如,使用声明性,但建立 Table
单独对象:
metadata_obj = Base.metadata
employees_table = Table(
'employee', metadata_obj,
Column('id', Integer, primary_key=True),
Column('name', String(50)),
)
managers_table = Table(
'manager', metadata_obj,
Column('id', Integer, primary_key=True),
Column('name', String(50)),
Column('manager_data', String(50)),
)
engineers_table = Table(
'engineer', metadata_obj,
Column('id', Integer, primary_key=True),
Column('name', String(50)),
Column('engineer_info', String(50)),
)
接下来,使用 polymorphic_union()
::
from sqlalchemy.orm import polymorphic_union
pjoin = polymorphic_union({
'employee': employees_table,
'manager': managers_table,
'engineer': engineers_table
}, 'type', 'pjoin')
与上述 Table
对象,映射可以使用“半经典”样式生成,其中我们将声明性与 __table__
论点;我们上面的多态联合是通过 __mapper_args__
到 mapper.with_polymorphic
参数::
class Employee(Base):
__table__ = employee_table
__mapper_args__ = {
'polymorphic_on': pjoin.c.type,
'with_polymorphic': ('*', pjoin),
'polymorphic_identity': 'employee'
}
class Engineer(Employee):
__table__ = engineer_table
__mapper_args__ = {
'polymorphic_identity': 'engineer',
'concrete': True}
class Manager(Employee):
__table__ = manager_table
__mapper_args__ = {
'polymorphic_identity': 'manager',
'concrete': True}
或者,相同 Table
对象可以完全“经典”样式使用,根本不使用声明性。类似于声明性提供的构造函数的说明如下:
class Employee(object):
def __init__(self, **kw):
for k in kw:
setattr(self, k, kw[k])
class Manager(Employee):
pass
class Engineer(Employee):
pass
employee_mapper = mapper_registry.map_imperatively(
Employee,
pjoin,
with_polymorphic=('*', pjoin),
polymorphic_on=pjoin.c.type,
)
manager_mapper = mapper_registry.map_imperatively(
Manager,
managers_table,
inherits=employee_mapper,
concrete=True,
polymorphic_identity='manager',
)
engineer_mapper = mapper_registry.map_imperatively(
Engineer,
engineers_table,
inherits=employee_mapper,
concrete=True,
polymorphic_identity='engineer',
)
“抽象”示例也可以使用“半经典”或“经典”样式进行映射。不同之处在于,不是将“多态结合”应用于 mapper.with_polymorphic
参数,我们直接将其应用于我们最基本的映射器上的映射可选项。半经典映射如下图所示:
from sqlalchemy.orm import polymorphic_union
pjoin = polymorphic_union({
'manager': managers_table,
'engineer': engineers_table
}, 'type', 'pjoin')
class Employee(Base):
__table__ = pjoin
__mapper_args__ = {
'polymorphic_on': pjoin.c.type,
'with_polymorphic': '*',
'polymorphic_identity': 'employee'
}
class Engineer(Employee):
__table__ = engineer_table
__mapper_args__ = {
'polymorphic_identity': 'engineer',
'concrete': True}
class Manager(Employee):
__table__ = manager_table
__mapper_args__ = {
'polymorphic_identity': 'manager',
'concrete': True}
在上面,我们使用 polymorphic_union()
使用与以前相同的方式,只是我们省略了 employee
桌子。
参见
命令(又称经典)映射 -“经典”映射的背景信息
在具体的继承场景中,映射关系具有挑战性,因为不同的类不共享表。如果关系只涉及特定的类,例如 Company
在前面的示例中, Manager
,不需要特殊步骤,因为这只是两个相关表。
然而,如果 Company
是一对多的关系 Employee
,指示集合可以同时包含 Engineer
和 Manager
对象,这意味着 Employee
必须具有多态加载功能,并且每个要关联的表都必须具有返回到 company
表。这种配置的示例如下:
from sqlalchemy.ext.declarative import ConcreteBase
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String(50))
employees = relationship("Employee")
class Employee(ConcreteBase, Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
company_id = Column(ForeignKey('company.id'))
__mapper_args__ = {
'polymorphic_identity': 'employee',
'concrete': True
}
class Manager(Employee):
__tablename__ = 'manager'
id = Column(Integer, primary_key=True)
name = Column(String(50))
manager_data = Column(String(40))
company_id = Column(ForeignKey('company.id'))
__mapper_args__ = {
'polymorphic_identity': 'manager',
'concrete': True
}
class Engineer(Employee):
__tablename__ = 'engineer'
id = Column(Integer, primary_key=True)
name = Column(String(50))
engineer_info = Column(String(40))
company_id = Column(ForeignKey('company.id'))
__mapper_args__ = {
'polymorphic_identity': 'engineer',
'concrete': True
}
The next complexity with concrete inheritance and relationships involves
when we'd like one or all of Employee
, Manager
and Engineer
to
themselves refer back to Company
. For this case, SQLAlchemy has
special behavior in that a relationship()
placed on Employee
which links to Company
does not work
against the Manager
and Engineer
classes, when exercised at the
instance level. Instead, a distinct
relationship()
must be applied to each class. In order to achieve
bi-directional behavior in terms of three separate relationships which
serve as the opposite of Company.employees
, the
relationship.back_populates
parameter is used between
each of the relationships:
from sqlalchemy.ext.declarative import ConcreteBase
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String(50))
employees = relationship("Employee", back_populates="company")
class Employee(ConcreteBase, Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
company_id = Column(ForeignKey('company.id'))
company = relationship("Company", back_populates="employees")
__mapper_args__ = {
'polymorphic_identity': 'employee',
'concrete': True
}
class Manager(Employee):
__tablename__ = 'manager'
id = Column(Integer, primary_key=True)
name = Column(String(50))
manager_data = Column(String(40))
company_id = Column(ForeignKey('company.id'))
company = relationship("Company", back_populates="employees")
__mapper_args__ = {
'polymorphic_identity': 'manager',
'concrete': True
}
class Engineer(Employee):
__tablename__ = 'engineer'
id = Column(Integer, primary_key=True)
name = Column(String(50))
engineer_info = Column(String(40))
company_id = Column(ForeignKey('company.id'))
company = relationship("Company", back_populates="employees")
__mapper_args__ = {
'polymorphic_identity': 'engineer',
'concrete': True
}
上述限制与当前的实现有关,包括具体的继承类不共享超类的任何属性,因此需要建立不同的关系。
使用具体继承进行加载的选项是有限的;通常,如果在映射器上使用一个声明性具体混合配置了多态加载,则在当前的sqlAlchemy版本中,不能在查询时对其进行修改。通常情况下, with_polymorphic()
函数可以覆盖混凝土使用的加载样式,但是由于当前的限制,目前还不支持这种方式。
flambé! the dragon and The Alchemist image designs created and generously donated by Rotem Yaari.
Created using Sphinx 4.2.0.