为继承映射编写 SELECT 语句¶
关于本文档
本节使用使用 ORM 继承功能配置的 ORM 映射,如映射类继承层次结构中所述。 重点将放在
Joined Table Inheritance,因为这是最复杂的 ORM 查询情况。
从基类 SELECT 与特定子类中选择¶
针对联接继承层次结构中的类构造的 SELECT 语句将查询该类映射到的表以及存在的任何超表,并使用 JOIN 将它们链接在一起。然后,查询将返回属于该请求类型的对象以及请求类型的任何子类型,使用每行中的鉴别器值来确定正确的类型。以下查询是针对 Manager
建立的
子类 Employee
的子类,然后返回一个结果,该结果将仅包含 Manager
类型的对象:
>>> from sqlalchemy import select
>>> stmt = select(Manager).order_by(Manager.id)
>>> managers = session.scalars(stmt).all()
BEGIN (implicit)
SELECT manager.id, employee.id AS id_1, employee.name, employee.type, employee.company_id, manager.manager_name
FROM employee JOIN manager ON employee.id = manager.id ORDER BY manager.id
[...] ()
>>> print(managers)
[Manager('Mr. Krabs')]
当 SELECT 语句针对层次结构中的基类时,
默认行为是,只有该类的表才会包含在
不会使用渲染的 SQL 和 JOIN。与所有情况一样,
discriminator 列用于区分不同的请求子类型,然后返回任何可能的子类型的对象。返回的对象将具有与填充的基表对应的属性,与子表对应的属性将以未加载状态开始,并在访问时自动加载。子属性的加载可以通过多种方式配置为更加“急切”,本节稍后将讨论。
下面的示例创建针对 Employee
超类的查询。这表示任何类型的对象(包括 Manager
、Engineer
和 Employee
)都可能位于结果集中:
>>> from sqlalchemy import select
>>> stmt = select(Employee).order_by(Employee.id)
>>> objects = session.scalars(stmt).all()
BEGIN (implicit)
SELECT employee.id, employee.name, employee.type, employee.company_id
FROM employee ORDER BY employee.id
[...] ()
>>> print(objects)
[Manager('Mr. Krabs'), Engineer('SpongeBob'), Engineer('Squidward')]
在上面,Manager
和 Engineer
的其他表未包含在 SELECT 中,这意味着返回的对象将不包含来自这些表的数据,在本例中为 .manager_name
属性
以及 Engineer
类的 .engineer_info
属性。 这些属性从
expired 状态,并在首次使用延迟加载访问时自动填充自身:
>>> mr_krabs = objects[0]
>>> print(mr_krabs.manager_name)
SELECT manager.manager_name AS manager_manager_name
FROM manager
WHERE ? = manager.id
[...] (1,)
Eugene H. Krabs
如果大量对象已被
loaded,以防使用应用程序需要访问
特定于子类的属性,因为这将是
N 加上一个每行发出额外 SQL 的问题。此附加 SQL 可能会影响性能,并且与使用 asyncio 等方法不兼容。 此外,在我们对
Employee
对象,因为查询仅针对基表,因此我们无法添加涉及 Manager
或 Engineer
等子类特定属性的 SQL 条件。接下来的两节详细介绍了两个
结构以不同的方式为这两个问题提供解决方案,则
selectin_polymorphic()
加载器选项和
with_polymorphic()
实体构造。
使用 selectin_polymorphic()¶
为了解决访问子类上的属性时的性能问题,可以使用 selectin_polymorphic()
加载器策略来
预先在许多
对象。 此 loader 选项的工作方式与
selectinload()
关系加载器策略,针对层次结构中加载的对象的每个子表发出额外的 SELECT 语句,使用 IN
根据主键查询其他行。
selectin_polymorphic()
接受正在查询的基本实体作为其参数,后跟该实体的一系列子类,应为传入行加载其特定属性:
>>> from sqlalchemy.orm import selectin_polymorphic
>>> loader_opt = selectin_polymorphic(Employee, [Manager, Engineer])
然后将 selectin_polymorphic()
结构用作加载器选项,将其传递给 Select
的 Select.options()
方法。该示例说明了如何使用 selectin_polymorphic()
预先加载 Manager
和 Engineer
子类的本地列:
>>> from sqlalchemy.orm import selectin_polymorphic
>>> loader_opt = selectin_polymorphic(Employee, [Manager, Engineer])
>>> stmt = select(Employee).order_by(Employee.id).options(loader_opt)
>>> objects = session.scalars(stmt).all()
BEGIN (implicit)
SELECT employee.id, employee.name, employee.type, employee.company_id
FROM employee ORDER BY employee.id
[...] ()
SELECT manager.id AS manager_id, employee.id AS employee_id,
employee.type AS employee_type, manager.manager_name AS manager_manager_name
FROM employee JOIN manager ON employee.id = manager.id
WHERE employee.id IN (?) ORDER BY employee.id
[...] (1,)
SELECT engineer.id AS engineer_id, employee.id AS employee_id,
employee.type AS employee_type, engineer.engineer_info AS engineer_engineer_info
FROM employee JOIN engineer ON employee.id = engineer.id
WHERE employee.id IN (?, ?) ORDER BY employee.id
[...] (2, 3)
>>> print(objects)
[Manager('Mr. Krabs'), Engineer('SpongeBob'), Engineer('Squidward')]
上面的示例说明了为了急切地获取其他属性(如 Engineer.engineer_info
以及 Manager.manager_name
。现在,我们可以在加载的对象上访问这些子属性,而无需发出任何其他 SQL 语句:
>>> print(objects[0].manager_name)
Eugene H. Krabs
提示
selectin_polymorphic()
loader选项尚未针对以下事实进行优化:基本员工
表不需要包含在后两个 “eager load” 查询中;因此,在上面的示例中,我们看到从 employee
到 manager
和 engineer
,即使已经加载了 employee
的列。这与 selectinload()
关系策略相反,后者在这方面更复杂,并且可以在不需要时分解出 JOIN。
将 selectin_polymorphic() 应用于现有的 Eager load¶
除了将 selectin_polymorphic()
指定为选项
对于由 statement 加载的顶级实体,我们还可以指示
selectin_polymorphic()
在现有负载的目标上。由于我们的设置映射包括一个父级
具有 Company.employees
关系
的 Company
实体()
引用 Employee
实体,我们可以说明针对 Company
实体的 SELECT,该实体通过应用 Load.selectin_polymorphic()
预先加载所有 Employee
对象及其子类型上的所有属性,如下所示
作为链式装载机选项;在此形式中,第一个参数是隐式的 from
前面的 loader 选项(在本例中为 selectInload()
),因此我们只指示要加载的其他目标子类:
>>> from sqlalchemy.orm import selectinload
>>> stmt = select(Company).options(
... selectinload(Company.employees).selectin_polymorphic([Manager, Engineer])
... )
>>> for company in session.scalars(stmt):
... print(f"company: {company.name}")
... print(f"employees: {company.employees}")
BEGIN (implicit)
SELECT company.id, company.name
FROM company
[...] ()
SELECT employee.company_id AS employee_company_id, employee.id AS employee_id,
employee.name AS employee_name, employee.type AS employee_type
FROM employee
WHERE employee.company_id IN (?)
[...] (1,)
SELECT manager.id AS manager_id, employee.id AS employee_id,
employee.type AS employee_type,
manager.manager_name AS manager_manager_name
FROM employee JOIN manager ON employee.id = manager.id
WHERE employee.id IN (?) ORDER BY employee.id
[...] (1,)
SELECT engineer.id AS engineer_id, employee.id AS employee_id,
employee.type AS employee_type,
engineer.engineer_info AS engineer_engineer_info
FROM employee JOIN engineer ON employee.id = engineer.id
WHERE employee.id IN (?, ?) ORDER BY employee.id
[...] (2, 3)
company: Krusty Krab
employees: [Manager('Mr. Krabs'), Engineer('SpongeBob'), Engineer('Squidward')]
另请参阅
多态子类型的预先加载 - 说明了使用 with_polymorphic()
代替的上述等效示例
将 loader 选项应用于 selectin_polymorphic 加载的子类¶
selectin_polymorphic()
发出的 SELECT 语句本身就是 ORM 语句,因此我们还可以添加其他引用特定子类的加载器选项(例如关系加载技术中记录的那些)。这些选项应作为
selectin_polymorphic()
选项,即
select.options()
中。
例如,如果我们认为 Manager
映射器与一个名为 Paperwork
的实体具有一对多的关系,我们可以结合使用
selectin_polymorphic()
和 selectinload()
在所有 Manager
对象上预先加载此集合,其中
管理器
对象本身也被急切地加载:
>>> from sqlalchemy.orm import selectin_polymorphic
>>> stmt = (
... select(Employee)
... .order_by(Employee.id)
... .options(
... selectin_polymorphic(Employee, [Manager, Engineer]),
... selectinload(Manager.paperwork),
... )
... )
>>> objects = session.scalars(stmt).all()
SELECT employee.id, employee.name, employee.type, employee.company_id
FROM employee ORDER BY employee.id
[...] ()
SELECT manager.id AS manager_id, employee.id AS employee_id, employee.type AS employee_type, manager.manager_name AS manager_manager_name
FROM employee JOIN manager ON employee.id = manager.id
WHERE employee.id IN (?) ORDER BY employee.id
[...] (1,)
SELECT paperwork.manager_id AS paperwork_manager_id, paperwork.id AS paperwork_id, paperwork.document_name AS paperwork_document_name
FROM paperwork
WHERE paperwork.manager_id IN (?)
[...] (1,)
SELECT engineer.id AS engineer_id, employee.id AS employee_id, employee.type AS employee_type, engineer.engineer_info AS engineer_engineer_info
FROM employee JOIN engineer ON employee.id = engineer.id
WHERE employee.id IN (?, ?) ORDER BY employee.id
[...] (2, 3)
>>> print(objects[0])
Manager('Mr. Krabs')
>>> print(objects[0].paperwork)
[Paperwork('Secret Recipes'), Paperwork('Krabby Patty Orders')]
当 selectin_polymorphic 本身是一个子选项时应用 loader 选项¶
2.0.21 版本的新Function。
上一节说明了 selectin_polymorphic()
和
selectInload()
用作同级选项,两者都在对 select.options()
的单个调用中使用。 如果目标实体是
已从父关系加载,如
将 selectin_polymorphic() 应用于现有的预先加载,我们可以使用适用的 Load.options()
方法来应用这个 “同级” 模式
子选项分配给父级,如
使用 Load.options() 指定 Sub-Options。下面我们结合两个示例来加载 Company.employees
,同时加载
Manager
和 Engineer
类,以及急切地加载
'Manager.paperwork'
属性:
>>> from sqlalchemy.orm import selectinload
>>> stmt = select(Company).options(
... selectinload(Company.employees).options(
... selectin_polymorphic(Employee, [Manager, Engineer]),
... selectinload(Manager.paperwork),
... )
... )
>>> for company in session.scalars(stmt):
... print(f"company: {company.name}")
... for employee in company.employees:
... if isinstance(employee, Manager):
... print(f"manager: {employee.name} paperwork: {employee.paperwork}")
BEGIN (implicit)
SELECT company.id, company.name
FROM company
[...] ()
SELECT employee.company_id AS employee_company_id, employee.id AS employee_id, employee.name AS employee_name, employee.type AS employee_type
FROM employee
WHERE employee.company_id IN (?)
[...] (1,)
SELECT manager.id AS manager_id, employee.id AS employee_id, employee.type AS employee_type, manager.manager_name AS manager_manager_name
FROM employee JOIN manager ON employee.id = manager.id
WHERE employee.id IN (?) ORDER BY employee.id
[...] (1,)
SELECT paperwork.manager_id AS paperwork_manager_id, paperwork.id AS paperwork_id, paperwork.document_name AS paperwork_document_name
FROM paperwork
WHERE paperwork.manager_id IN (?)
[...] (1,)
SELECT engineer.id AS engineer_id, employee.id AS employee_id, employee.type AS employee_type, engineer.engineer_info AS engineer_engineer_info
FROM employee JOIN engineer ON employee.id = engineer.id
WHERE employee.id IN (?, ?) ORDER BY employee.id
[...] (2, 3)
company: Krusty Krab
manager: Mr. Krabs paperwork: [Paperwork('Secret Recipes'), Paperwork('Krabby Patty Orders')]
在 mapper 上配置 selectin_polymorphic()¶
selectin_polymorphic()
的行为可以在特定的
mappers 的 mapper,以便默认情况下使用
Mapper.polymorphic_load
参数,使用值 “selectin”
基于每个子类。 下面的示例说明了 this 的用法
参数中:
class Employee(Base):
__tablename__ = "employee"
id = mapped_column(Integer, primary_key=True)
name = mapped_column(String(50))
type = mapped_column(String(50))
__mapper_args__ = {"polymorphic_identity": "employee", "polymorphic_on": type}
class Engineer(Employee):
__tablename__ = "engineer"
id = mapped_column(Integer, ForeignKey("employee.id"), primary_key=True)
engineer_info = mapped_column(String(30))
__mapper_args__ = {
"polymorphic_load": "selectin",
"polymorphic_identity": "engineer",
}
class Manager(Employee):
__tablename__ = "manager"
id = mapped_column(Integer, ForeignKey("employee.id"), primary_key=True)
manager_name = mapped_column(String(30))
__mapper_args__ = {
"polymorphic_load": "selectin",
"polymorphic_identity": "manager",
}
通过上述映射,针对 Employee
类的 SELECT 语句将
自动假定使用
selectin_polymorphic(Employee, [Engineer, Manager])
作为 loader 选项。
使用 with_polymorphic()¶
与仅影响对象加载的 selectin_polymorphic()
相比,with_polymorphic()
结构会影响多态结构的 SQL 查询的呈现方式,最常见的是每个包含的子表的一系列 LEFT OUTER JOIN。这种连接结构称为多态可选。通过一次提供多个子表的视图,with_polymorphic()
提供了一种一次跨多个继承类编写 SELECT 语句的方法,并能够根据单个子表添加过滤条件。
with_polymorphic()
本质上是
aliased()
结构。它接受类似于 selectin_polymorphic()
的形式作为其参数,这是正在查询的基本实体,后跟该实体的一系列子类,应为传入行加载其特定属性:
>>> from sqlalchemy.orm import with_polymorphic
>>> employee_poly = with_polymorphic(Employee, [Engineer, Manager])
为了指示所有子类都应该是实体的一部分,
with_polymorphic()
还将接受字符串 “*”
,该字符串可以代替类序列来表示所有类(请注意,selectin_polymorphic()
尚不支持此功能):
>>> employee_poly = with_polymorphic(Employee, "*")
以下示例说明了与上一节中所示相同的作,即同时加载 Manager
和 Engineer
的所有列:
>>> stmt = select(employee_poly).order_by(employee_poly.id)
>>> objects = session.scalars(stmt).all()
BEGIN (implicit)
SELECT employee.id, employee.name, employee.type, employee.company_id,
manager.id AS id_1, manager.manager_name, engineer.id AS id_2, engineer.engineer_info
FROM employee
LEFT OUTER JOIN manager ON employee.id = manager.id
LEFT OUTER JOIN engineer ON employee.id = engineer.id ORDER BY employee.id
[...] ()
>>> print(objects)
[Manager('Mr. Krabs'), Engineer('SpongeBob'), Engineer('Squidward')]
与 selectin_polymorphic()
的情况一样,子类上的属性已经加载:
>>> print(objects[0].manager_name)
Eugene H. Krabs
作为 with_polymorphic()
生成的默认可选
使用 LEFT OUTER JOIN,从数据库的角度来看,查询并不好
优化为 selectin_polymorphic()
采用的方法,使用简单的 SELECT 语句仅使用基于每个表发出的 JOIN。
使用 with_polymorphic() 过滤子类属性¶
with_polymorphic()
构造通过包含允许引用子类的命名空间,使包含的子类映射器上的属性可用。在上一节中创建的 employee_poly
构造包括名为 的属性。Engineer
和 .经理
它们根据多态 SELECT 为 Engineer
和 Manager
提供命名空间。在下面的示例中,我们可以使用 or_()
construct 同时针对两个类创建条件:
>>> from sqlalchemy import or_
>>> employee_poly = with_polymorphic(Employee, [Engineer, Manager])
>>> stmt = (
... select(employee_poly)
... .where(
... or_(
... employee_poly.Manager.manager_name == "Eugene H. Krabs",
... employee_poly.Engineer.engineer_info
... == "Senior Customer Engagement Engineer",
... )
... )
... .order_by(employee_poly.id)
... )
>>> objects = session.scalars(stmt).all()
SELECT employee.id, employee.name, employee.type, employee.company_id, manager.id AS id_1,
manager.manager_name, engineer.id AS id_2, engineer.engineer_info
FROM employee
LEFT OUTER JOIN manager ON employee.id = manager.id
LEFT OUTER JOIN engineer ON employee.id = engineer.id
WHERE manager.manager_name = ? OR engineer.engineer_info = ?
ORDER BY employee.id
[...] ('Eugene H. Krabs', 'Senior Customer Engagement Engineer')
>>> print(objects)
[Manager('Mr. Krabs'), Engineer('Squidward')]
在 with_polymorphic 中使用别名¶
with_polymorphic()
结构,作为
aliased()
也提供了 aliased()
does,这是多态 selectable 本身的 “aliasing” 的 “aliasing” 的 “aliasing” 的 set。
具体来说,这意味着两个或多个 with_polymorphic()
实体,引用相同的类层次结构,可以同时在单个语句中使用。
要将此功能与联接的继承映射一起使用,我们通常需要传递两个参数,with_polymorphic.aliased
以及
with_polymorphic.flat
的with_polymorphic.aliased
parameter 指示应引用多态 selectable
通过此构造唯一的别名。 这
with_polymorphic.flat
参数特定于默认的 LEFT OUTER JOIN 多态可选,并指示应在语句中使用更优化的别名形式。
为了说明此功能,以下示例为两个单独的多态实体发出 SELECT,即 Employee
joined with Engineer
和 Employee
joined with Manager
。由于这两个多态实体都将在其多态可选内容中包含基本 employee
表,因此必须应用别名才能在两个不同的上下文中区分此表。这两个多态实体被视为两个单独的表,因此通常需要以某种方式相互联接,如下图所示,实体在 company_id
上联接
列以及针对
员工
/ 经理
实体:
>>> manager_employee = with_polymorphic(Employee, [Manager], aliased=True, flat=True)
>>> engineer_employee = with_polymorphic(Employee, [Engineer], aliased=True, flat=True)
>>> stmt = (
... select(manager_employee, engineer_employee)
... .join(
... engineer_employee,
... engineer_employee.company_id == manager_employee.company_id,
... )
... .where(
... or_(
... manager_employee.name == "Mr. Krabs",
... manager_employee.Manager.manager_name == "Eugene H. Krabs",
... )
... )
... .order_by(engineer_employee.name, manager_employee.name)
... )
>>> for manager, engineer in session.execute(stmt):
... print(f"{manager} {engineer}")
SELECT
employee_1.id, employee_1.name, employee_1.type, employee_1.company_id,
manager_1.id AS id_1, manager_1.manager_name,
employee_2.id AS id_2, employee_2.name AS name_1, employee_2.type AS type_1,
employee_2.company_id AS company_id_1, engineer_1.id AS id_3, engineer_1.engineer_info
FROM employee AS employee_1
LEFT OUTER JOIN manager AS manager_1 ON employee_1.id = manager_1.id
JOIN
(employee AS employee_2 LEFT OUTER JOIN engineer AS engineer_1 ON employee_2.id = engineer_1.id)
ON employee_2.company_id = employee_1.company_id
WHERE employee_1.name = ? OR manager_1.manager_name = ?
ORDER BY employee_2.name, employee_1.name
[...] ('Mr. Krabs', 'Eugene H. Krabs')
Manager('Mr. Krabs') Manager('Mr. Krabs')
Manager('Mr. Krabs') Engineer('SpongeBob')
Manager('Mr. Krabs') Engineer('Squidward')
在上面的示例中,with_polymorphic.flat
的行为
是多态可选项保持为它们的 LEFT OUTER JOIN
单个表,它们本身被赋予匿名别名。 那里
也是生成的右嵌套 JOIN。
当省略 with_polymorphic.flat
参数时,通常的行为是每个多态可选内容都包含在一个子查询中,从而产生更详细的形式:
>>> manager_employee = with_polymorphic(Employee, [Manager], aliased=True)
>>> engineer_employee = with_polymorphic(Employee, [Engineer], aliased=True)
>>> stmt = (
... select(manager_employee, engineer_employee)
... .join(
... engineer_employee,
... engineer_employee.company_id == manager_employee.company_id,
... )
... .where(
... or_(
... manager_employee.name == "Mr. Krabs",
... manager_employee.Manager.manager_name == "Eugene H. Krabs",
... )
... )
... .order_by(engineer_employee.name, manager_employee.name)
... )
>>> print(stmt)
SELECT anon_1.employee_id, anon_1.employee_name, anon_1.employee_type,
anon_1.employee_company_id, anon_1.manager_id, anon_1.manager_manager_name, anon_2.employee_id AS employee_id_1,
anon_2.employee_name AS employee_name_1, anon_2.employee_type AS employee_type_1,
anon_2.employee_company_id AS employee_company_id_1, anon_2.engineer_id, anon_2.engineer_engineer_info
FROM
(SELECT employee.id AS employee_id, employee.name AS employee_name, employee.type AS employee_type,
employee.company_id AS employee_company_id,
manager.id AS manager_id, manager.manager_name AS manager_manager_name
FROM employee LEFT OUTER JOIN manager ON employee.id = manager.id) AS anon_1
JOIN
(SELECT employee.id AS employee_id, employee.name AS employee_name, employee.type AS employee_type,
employee.company_id AS employee_company_id, engineer.id AS engineer_id, engineer.engineer_info AS engineer_engineer_info
FROM employee LEFT OUTER JOIN engineer ON employee.id = engineer.id) AS anon_2
ON anon_2.employee_company_id = anon_1.employee_company_id
WHERE anon_1.employee_name = :employee_name_2 OR anon_1.manager_manager_name = :manager_manager_name_1
ORDER BY anon_2.employee_name, anon_1.employee_name
从历史上看,上述形式更容易移植到不一定支持右嵌套 JOIN 的后端,并且当 with_polymorphic()
使用的“多态可选”不是时,它可能也适用
表的简单 LEFT OUTER JOIN,就像使用诸如
具体的表继承映射,以及通常使用替代多态可选项时。
在 mapper 上配置 with_polymorphic()¶
与 selectin_polymorphic()
一样,
with_polymorphic()
构造还支持映射器配置的版本,该版本可以通过两种不同的方式进行配置,要么使用 mapper.with_polymorphic
参数在基类上,要么在每个子类的基础上使用 Mapper.polymorphic_load
参数以更现代的形式,传递值 “inline”。
警告
对于联接的继承映射,最好显式使用
with_polymorphic()
在查询中,或者对于隐式的 Eager 子类加载,请使用 Mapper.polymorphic_load
和 “selectin”
,而不是使用 mapper 级别的 mapper.with_polymorphic
参数。此参数调用 complex
旨在重写 SELECT 语句中的 FROM 子句的启发式方法
这可能会干扰更复杂的语句的构造,
特别是那些具有引用同一映射
实体。
例如,我们可以使用
Mapper.polymorphic_load
为 “inline”
,如下所示:
class Employee(Base):
__tablename__ = "employee"
id = mapped_column(Integer, primary_key=True)
name = mapped_column(String(50))
type = mapped_column(String(50))
__mapper_args__ = {"polymorphic_identity": "employee", "polymorphic_on": type}
class Engineer(Employee):
__tablename__ = "engineer"
id = mapped_column(Integer, ForeignKey("employee.id"), primary_key=True)
engineer_info = mapped_column(String(30))
__mapper_args__ = {
"polymorphic_load": "inline",
"polymorphic_identity": "engineer",
}
class Manager(Employee):
__tablename__ = "manager"
id = mapped_column(Integer, ForeignKey("employee.id"), primary_key=True)
manager_name = mapped_column(String(30))
__mapper_args__ = {
"polymorphic_load": "inline",
"polymorphic_identity": "manager",
}
通过上述映射,针对 Employee
类的 SELECT 语句将
自动假定使用
with_polymorphic(Employee, [Engineer, Manager])
作为主实体:
print(select(Employee))
SELECT employee.id, employee.name, employee.type, engineer.id AS id_1,
engineer.engineer_info, manager.id AS id_2, manager.manager_name
FROM employee
LEFT OUTER JOIN engineer ON employee.id = engineer.id
LEFT OUTER JOIN manager ON employee.id = manager.id
当使用 mapper 级别的 “with polymorphic” 时,查询还可以引用
子类实体,其中它们隐式表示连接的表
在多态查询中。 以上,我们可以自由地参考
经理
和工程师
直接针对默认员工
实体:
print(
select(Employee).where(
or_(Manager.manager_name == "x", Engineer.engineer_info == "y")
)
)
SELECT employee.id, employee.name, employee.type, engineer.id AS id_1,
engineer.engineer_info, manager.id AS id_2, manager.manager_name
FROM employee
LEFT OUTER JOIN engineer ON employee.id = engineer.id
LEFT OUTER JOIN manager ON employee.id = manager.id
WHERE manager.manager_name = :manager_name_1
OR engineer.engineer_info = :engineer_info_1
但是,如果我们需要引用 Employee
实体或其 sub
实体,我们将再次直接使用
with_polymorphic()
定义这些别名实体,如将别名与with_polymorphic结合使用中所示。
为了更集中地控制多态可选对象,更遗留的
可以使用 Mapper 级多态控制的形式,即
Mapper.with_polymorphic
参数,在基
类。此参数接受与
with_polymorphic()
结构,但是与联接的继承映射一起使用的是普通星号,表示所有子表都应该 LEFT OUTER JOINED,如下所示:
class Employee(Base):
__tablename__ = "employee"
id = mapped_column(Integer, primary_key=True)
name = mapped_column(String(50))
type = mapped_column(String(50))
__mapper_args__ = {
"polymorphic_identity": "employee",
"with_polymorphic": "*",
"polymorphic_on": type,
}
class Engineer(Employee):
__tablename__ = "engineer"
id = mapped_column(Integer, ForeignKey("employee.id"), primary_key=True)
engineer_info = mapped_column(String(30))
__mapper_args__ = {
"polymorphic_identity": "engineer",
}
class Manager(Employee):
__tablename__ = "manager"
id = mapped_column(Integer, ForeignKey("employee.id"), primary_key=True)
manager_name = mapped_column(String(30))
__mapper_args__ = {
"polymorphic_identity": "manager",
}
总的来说,with_polymorphic()
和选项(如 Mapper.with_polymorphic
使用的 LEFT OUTER JOIN 格式可能很麻烦
从 SQL 和数据库优化器的角度来看;用于一般负载
子类属性中,则
selectin_polymorphic()
方法,或者相当于将 Mapper.polymorphic_load
设置为 “selectin”
的 mapper 级别,应该更可取,仅在需要时在每个查询的基础上使用 with_polymorphic(
)。
连接到特定的子类型或 with_polymorphic() 实体¶
由于 with_polymorphic()
实体是 aliased()
的一种特殊情况,为了将多态实体视为连接的目标,特别是当使用 relationship()
构造作为 ON 子句时,
我们对常规别名使用相同的技术,详见
使用 Relationship 在别名目标之间进行连接,最简洁的是 PropComparator.of_type()。
在下面的示例中,我们说明了父 Company
实体沿一对多关系的联接
Company.employees
中配置,该
setup 链接到 Employee
对象,使用 with_polymorphic()
实体作为目标:
>>> employee_plus_engineer = with_polymorphic(Employee, [Engineer])
>>> stmt = (
... select(Company.name, employee_plus_engineer.name)
... .join(Company.employees.of_type(employee_plus_engineer))
... .where(
... or_(
... employee_plus_engineer.name == "SpongeBob",
... employee_plus_engineer.Engineer.engineer_info
... == "Senior Customer Engagement Engineer",
... )
... )
... )
>>> for company_name, emp_name in session.execute(stmt):
... print(f"{company_name} {emp_name}")
SELECT company.name, employee.name AS name_1
FROM company JOIN (employee LEFT OUTER JOIN engineer ON employee.id = engineer.id) ON company.id = employee.company_id
WHERE employee.name = ? OR engineer.engineer_info = ?
[...] ('SpongeBob', 'Senior Customer Engagement Engineer')
Krusty Krab SpongeBob
Krusty Krab Squidward
更直接地说,PropComparator.of_type()
还与任何类型的继承映射一起使用,以将沿 relationship()
的连接限制为 relationship()
目标的特定子类型。上述查询可以严格按照工程师
目标编写,如下所示:
>>> stmt = (
... select(Company.name, Engineer.name)
... .join(Company.employees.of_type(Engineer))
... .where(
... or_(
... Engineer.name == "SpongeBob",
... Engineer.engineer_info == "Senior Customer Engagement Engineer",
... )
... )
... )
>>> for company_name, emp_name in session.execute(stmt):
... print(f"{company_name} {emp_name}")
SELECT company.name, employee.name AS name_1
FROM company JOIN (employee JOIN engineer ON employee.id = engineer.id) ON company.id = employee.company_id
WHERE employee.name = ? OR engineer.engineer_info = ?
[...] ('SpongeBob', 'Senior Customer Engagement Engineer')
Krusty Krab SpongeBob
Krusty Krab Squidward
从上面可以看出,直接加入工程师
目标,而不是 with_polymorphic(Employee, [Engineer])
具有使用内部 JOIN 而不是
LEFT OUTER JOIN,SQL 优化器的性能通常更高
观点。
多态子类型的预先加载¶
PropComparator.of_type()
的用法与
Select.join()
方法也可以等效地应用于关系加载器选项,例如 selectinload()
和 joinedload()。
举个基本的例子,如果我们希望加载 Company
对象,并使用
with_polymorphic()
结构,我们可以写成:
>>> all_employees = with_polymorphic(Employee, "*")
>>> stmt = select(Company).options(selectinload(Company.employees.of_type(all_employees)))
>>> for company in session.scalars(stmt):
... print(f"company: {company.name}")
... print(f"employees: {company.employees}")
SELECT company.id, company.name
FROM company
[...] ()
SELECT employee.company_id AS employee_company_id, employee.id AS employee_id,
employee.name AS employee_name, employee.type AS employee_type, manager.id AS manager_id,
manager.manager_name AS manager_manager_name, engineer.id AS engineer_id,
engineer.engineer_info AS engineer_engineer_info
FROM employee
LEFT OUTER JOIN manager ON employee.id = manager.id
LEFT OUTER JOIN engineer ON employee.id = engineer.id
WHERE employee.company_id IN (?)
[...] (1,)
company: Krusty Krab
employees: [Manager('Mr. Krabs'), Engineer('SpongeBob'), Engineer('Squidward')]
上面的查询可以直接与
selectin_polymorphic()
版本在上一节 将 selectin_polymorphic() 应用于现有的 Eager Load 中进行了说明。
另请参阅
将 selectin_polymorphic() 应用于现有的预先加载 - 说明了使用 selectin_polymorphic()
代替的上述等效示例
用于单继承映射的 SELECT 语句¶
与联接的继承映射相比,单个继承映射的 SELECT 语句的构造往往更简单,因为对于全单继承层次结构,只有一个表。
无论继承层次结构是全单继承还是混合了联接继承和单继承,单继承的 SELECT 语句都通过使用其他 WHERE 条件限制 SELECT 语句来区分针对基类和子类的查询。
例如,对
Employee
将加载 Manager
、Engineer
和
员工
使用表的简单 SELECT:
>>> stmt = select(Employee).order_by(Employee.id)
>>> for obj in session.scalars(stmt):
... print(f"{obj}")
BEGIN (implicit)
SELECT employee.id, employee.name, employee.type
FROM employee ORDER BY employee.id
[...] ()
Manager('Mr. Krabs')
Engineer('SpongeBob')
Engineer('Squidward')
当为特定子类发出负载时,将向 SELECT 添加其他条件以限制行,例如在下面对 Engineer
实体执行 SELECT 的位置:
>>> stmt = select(Engineer).order_by(Engineer.id)
>>> objects = session.scalars(stmt).all()
SELECT employee.id, employee.name, employee.type, employee.engineer_info
FROM employee
WHERE employee.type IN (?) ORDER BY employee.id
[...] ('engineer',)
>>> for obj in objects:
... print(f"{obj}")
Engineer('SpongeBob')
Engineer('Squidward')
优化 Single Inheritance 的属性加载¶
关于如何 SELECT 子类上的属性的单个继承 Map 的默认行为类似于连接继承的默认行为,因为默认情况下特定于子类的属性仍会发出第二个 SELECT。在下面的示例中,加载了 Manager
类型的单个 Employee
,但是由于请求的类是 Employee
,因此 Manager.manager_name
attribute 默认不存在,并且会发出额外的 SELECT
访问时:
>>> mr_krabs = session.scalars(select(Employee).where(Employee.name == "Mr. Krabs")).one()
BEGIN (implicit)
SELECT employee.id, employee.name, employee.type
FROM employee
WHERE employee.name = ?
[...] ('Mr. Krabs',)
>>> mr_krabs.manager_name
SELECT employee.manager_name AS employee_manager_name
FROM employee
WHERE employee.id = ? AND employee.type IN (?)
[...] (1, 'manager')
'Eugene H. Krabs'
为了改变这种行为,用于预先加载联接继承加载中使用的这些附加属性的一般概念也适用于单个继承,包括使用 selectin_polymorphic()
选项以及 with_polymorphic()
选项,后者仅包含额外的列,从 SQL 的角度来看,对于单继承映射器来说效率更高:
>>> employees = with_polymorphic(Employee, "*")
>>> stmt = select(employees).order_by(employees.id)
>>> objects = session.scalars(stmt).all()
BEGIN (implicit)
SELECT employee.id, employee.name, employee.type,
employee.manager_name, employee.engineer_info
FROM employee ORDER BY employee.id
[...] ()
>>> for obj in objects:
... print(f"{obj}")
Manager('Mr. Krabs')
Engineer('SpongeBob')
Engineer('Squidward')
>>> objects[0].manager_name
'Eugene H. Krabs'
由于加载单继承子类映射的开销通常是最小的,因此建议单继承映射包括 Mapper.polymorphic_load
参数,并为那些
加载其特定
子类属性应该是通用的。 一个示例
setup(已修改为包含此选项)如下所示:
>>> class Base(DeclarativeBase):
... pass
>>> class Employee(Base):
... __tablename__ = "employee"
... id: Mapped[int] = mapped_column(primary_key=True)
... name: Mapped[str]
... type: Mapped[str]
...
... def __repr__(self):
... return f"{self.__class__.__name__}({self.name!r})"
...
... __mapper_args__ = {
... "polymorphic_identity": "employee",
... "polymorphic_on": "type",
... }
>>> class Manager(Employee):
... manager_name: Mapped[str] = mapped_column(nullable=True)
... __mapper_args__ = {
... "polymorphic_identity": "manager",
... "polymorphic_load": "inline",
... }
>>> class Engineer(Employee):
... engineer_info: Mapped[str] = mapped_column(nullable=True)
... __mapper_args__ = {
... "polymorphic_identity": "engineer",
... "polymorphic_load": "inline",
... }
通过上述映射,Manager
和 Engineer
类的列将包含在针对 Employee
的 SELECT 语句中
实体自动:
>>> print(select(Employee))
SELECT employee.id, employee.name, employee.type,
employee.manager_name, employee.engineer_info
FROM employee
继承加载 API¶
对象名称 |
描述 |
---|---|
|
|
|
|
-
函数 sqlalchemy.orm 中。with_polymorphic(base:Type[_O]Mapper[_O], classes:Literal['*']Iterable[Type[Any]], selectable:Literal[False,None]FromClause=False, flat: bool = False, polymorphic_on:ColumnElement[Any]None=None, 别名: bool = False, innerjoin: bool = False, adapt_on_names: bool = False, name:strNone=None, _use_mapper_path: bool = False)AliasedClass[_O] ¶
生成一个AliasedClass
构造,该构造为给定基的后代映射器指定列。
使用此方法将确保每个后代映射器的 table 都包含在 FROM 子句中,并允许对这些 table 使用 filter() 标准。生成的实例也将加载这些列,因此不需要这些列的“后提取”。
另请参阅
使用 with_polymorphic() - 完整讨论with_polymorphic()
的
参数
base¶ – 要别名的基类。
类¶—— 从基类继承的单个类或映射器,或类/映射器列表。或者,它也可以是字符串'*'
,在这种情况下,所有降序映射类都将添加到 FROM 子句中。
aliased¶ —— 当 True 时,可选对象将被别名。对于 JOIN,这意味着 JOIN 将从子查询内部 SELECT,除非with_polymorphic.flat
标志设置为 True,建议用于更简单的用例。
flat¶ —— 布尔值,将传递给FromClause.alias()
调用,以便Join
对象将为 Join 中的各个表设置别名,而不是创建子查询。所有现代数据库通常都支持右嵌套联接,并且通常会产生更高效的查询。只要生成的 SQL 正常运行,就建议设置此标志。
可选¶ –
将用于代替生成的 FROM 子句的表或子查询。如果任何所需的类使用具体的表继承,则此参数是必需的,因为 SQLAlchemy 当前无法在表之间自动生成 UNION。如果使用,则 selectable
参数必须表示每个映射类映射的完整表和列集。否则,未记账的映射列将导致其表直接附加到 FROM 子句中,这通常会导致不正确的结果。
当保留其默认值False
时,分配给基本映射器的多态可选项用于选择行。但是,它也可以作为None
传递,这将绕过配置的多态 selectable,而是为给定的目标类构造一个 ad-hoc selectable;对于 Joined Table Inheritance,这将是一个包含所有 Target Mapper 及其子类的 Join。
polymorphic_on¶ – 用作给定可选对象的 “鉴别器” 列的列。如果未给出,则将使用基类映射器的 polymorphic_on 属性(如果有)。这对于默认情况下没有多态加载行为的映射非常有用。
innerjoin¶——如果为 True,则将使用 INNER JOIN。仅当仅查询一个特定子类型时,才应指定此项adapt_on_names¶ –
通过aliased.adapt_on_names
参数添加到别名对象中。 这在以下情况下可能很有用 给定的 selectable 与现有的 map 没有直接关系 选择。
在 1.4.33 版本加入.
名称¶–
为生成的AliasedClass
指定的名称。
在 2.0.31 版本加入.
-
函数 sqlalchemy.orm 中。selectin_polymorphic(base_cls: _EntityType[Any], classes: Iterable[Type[Any]])_AbstractLoad ¶
指示应该对特定于子类的所有属性进行预先加载。
这对所有匹配的主键值使用带 IN 的额外 SELECT,并且是每个查询的类似物“selectin”
mapper.polymorphic_load
参数上的设置。
在 1.2 版本加入.