为继承映射编写 SELECT 语句

关于此文档

本节使用使用 ORM 继承 特性配置的 ORM 映射,在 映射类继承层次结构 中描述。重点将放在 连接表继承 上,因为这是最复杂的 ORM 查询情况。

查看此页面的 ORM 设置.

从基类选择 vs. 选择特定子类

针对连接继承层次结构中的类构建的 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。在所有情况下,鉴别符 列用于区分不同的请求子类型,这会导致返回任何可能的子类型的对象。返回的对象将具有对应于基表的填充属性,并且对应于子表的属性将处于未加载状态,并在访问时自动加载。子属性的加载可以配置为更“急切”,将在本节后面讨论。

以下示例创建了针对 Employee 超类的查询。这表示结果集中可能包含任何类型的对象,包括 ManagerEngineerEmployee

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

在上面,ManagerEngineer 的附加表没有包含在 SELECT 中,这意味着返回的对象将不包含这些表中表示的数据,在本例中是 Manager 类的 .manager_name 属性以及 Engineer 类的 .engineer_info 属性。这些属性最初处于 过期 状态,并将使用 延迟加载 在第一次访问时自动填充自身。

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

如果已加载大量对象,这种延迟加载行为并不可取,因为在使用应用程序需要访问特定于子类的属性的情况下,这将是导致 N+1 问题的示例,该问题将为每一行发出额外的 SQL。此额外的 SQL 会影响性能,并且可能与使用 asyncio 等方法不兼容。此外,在我们针对 Employee 对象的查询中,由于查询仅针对基表,因此我们没有办法在 ManagerEngineer 方面添加包含特定于子类属性的 SQL 条件。接下来的两节详细介绍了两种提供不同解决方案的构造,分别是 selectin_polymorphic() 加载器选项和 with_polymorphic() 实体构造。

使用 selectin_polymorphic()

为了解决访问子类属性时的性能问题,可以将 selectin_polymorphic() 加载策略用于 急切加载 这些额外的属性,以便一次为多个对象加载这些属性。此加载器选项的工作方式类似于 selectinload() 关系加载器策略,它将针对每个子表的附加 SELECT 语句发出,使用 IN 根据主键查询额外的行。

selectin_polymorphic() 将基实体作为参数,以及该实体的子类序列,以便为传入的行加载其特定属性。

>>> from sqlalchemy.orm import selectin_polymorphic
>>> loader_opt = selectin_polymorphic(Employee, [Manager, Engineer])

然后将 selectin_polymorphic() 构造用作加载器选项,将其传递给 Select.options() 方法 Select。此示例说明了使用 selectin_polymorphic() 急切加载 ManagerEngineer 子类的本地列。

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

上面的示例说明了发出两个额外的 SELECT 语句,以便急切地获取额外的属性,例如 Engineer.engineer_info 以及 Manager.manager_name。现在,我们可以在加载的对象上访问这些子属性,而不会发出任何额外的 SQL 语句。

>>> print(objects[0].manager_name)
Eugene H. Krabs

提示

selectin_polymorphic() 加载器选项尚未针对以下事实进行优化:基 employee 表不需要包含在最后两个“急切加载”查询中;因此,在上面的示例中,我们看到从 employeemanagerengineer 的 JOIN,即使 employee 中的列已经加载了。这与 selectinload() 关系策略形成对比,该策略在这方面更加复杂,可以在不需要时排除 JOIN。

将 selectin_polymorphic() 应用于现有急切加载

除了将selectin_polymorphic()指定为语句加载的顶级实体的选项外,我们也可以在现有加载的目标上指定selectin_polymorphic()。正如我们的设置映射包含一个父Company实体,该实体具有一个引用Employee实体的Company.employees relationship(),我们可以说明一个针对Company实体的SELECT,该SELECT 同时急切加载所有Employee对象以及它们子类型的所有属性,如下所示,通过将Load.selectin_polymorphic()用作链接的加载器选项;在这种形式中,第一个参数是之前加载器选项(在本例中为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() 而不是

将加载器选项应用于 selectin_polymorphic 加载的子类

selectin_polymorphic() 发出的 SELECT 语句本身就是 ORM 语句,因此我们也可以添加其他加载器选项(例如在 关系加载技术 中记录的那些选项),这些选项引用特定的子类。这些选项应作为 selectin_polymorphic() 选项的**兄弟姐妹**应用,也就是说,在 select.options() 中以逗号分隔。

例如,如果我们认为 Manager 映射器与一个名为 Paperwork 的实体具有 一对多 关系,我们可以结合使用 selectin_polymorphic()selectinload() 来急切地加载所有 Manager 对象上的此集合,其中 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 本身是子选项时应用加载器选项

版本 2.0.21 中的新增功能。

上一节说明了 selectin_polymorphic()selectinload() 用作兄弟姐妹选项,两者都在对 select.options() 的单个调用中使用。如果目标实体是已从父关系加载的实体,如 将 selectin_polymorphic() 应用于现有急切加载 中的示例所示,我们可以使用 Load.options() 方法应用此“兄弟姐妹”模式,该方法将子选项应用于父项,如 使用 Load.options() 指定子选项 中所示。下面我们将这两个示例结合起来加载 Company.employees,同时还加载 ManagerEngineer 类的属性,以及急切地加载 `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')]

在映射器上配置 selectin_polymorphic()

可以使用 Mapper.polymorphic_load 参数在特定映射器上配置 selectin_polymorphic() 的行为,以便它默认发生,在每个子类上使用值 "selectin"。以下示例说明了在 EngineerManager 子类中使用此参数。

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]) 作为加载器选项。

使用 with_polymorphic()

selectin_polymorphic() 仅影响对象的加载不同,with_polymorphic() 结构影响多态结构的 SQL 查询的呈现方式,最常见的是作为一系列 LEFT OUTER JOINs 到每个包含的子表。这种连接结构被称为**多态可选择对象**。通过提供对多个子表的同时视图,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, "*")

以下示例说明了与上一节中说明的相同操作,一次加载 ManagerEngineer 的所有列。

>>> 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() 所采用的方法,后者使用仅在每个表的基础上发出的简单的仅使用 JOINs 的 SELECT 语句。

使用 with_polymorphic() 过滤子类属性

The with_polymorphic() 结构使包含的子类映射器上的属性可用,方法是包括允许引用子类的命名空间。在上一节中创建的 employee_poly 结构包含名为 .Engineer.Manager 的属性,这些属性提供了多态 SELECT 中的 EngineerManager 的命名空间。在下面的示例中,我们可以使用 or_() 结构对两个类同时创建条件。

>>> 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() 的基本功能,即对多态可选择项本身进行“别名”。具体来说,这意味着两个或多个引用相同类层次结构的 with_polymorphic() 实体可以在单个语句中同时使用。

为了在连接继承映射中使用此功能,我们通常希望传递两个参数,with_polymorphic.aliased 以及 with_polymorphic.flatwith_polymorphic.aliased 参数表示多态可选择项应该由一个对该结构唯一的别名引用。 with_polymorphic.flat 参数特定于默认的 LEFT OUTER JOIN 多态可选择项,并指示应该在语句中使用更优化的别名形式。

为了说明此功能,以下示例发出了对两个独立的多态实体的 SELECT,EmployeeEngineer 连接,以及 EmployeeManager 连接。由于这两个多态实体都将在其多态可选择项中包含基本 employee 表,因此必须应用别名才能在其两个不同的上下文中区分该表。这两个多态实体被视为两个独立的表,因此通常需要以某种方式相互连接,如下所示,其中实体在 company_id 列上连接,以及对 Employee / Manager 实体的一些额外的限制条件

>>> 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 时,它也可能适用,例如使用 具体表继承 映射以及在一般情况下使用替代的多态可选择项时。

在映射器上配置 with_polymorphic()

selectin_polymorphic() 一样,with_polymorphic() 结构也支持映射器配置版本,它可以通过两种不同的方式进行配置,或者在基类上使用 mapper.with_polymorphic 参数,或者在更现代的形式中使用 Mapper.polymorphic_load 参数,在每个子类基础上,传递值 "inline"

警告

对于连接继承映射,建议在查询中显式使用 with_polymorphic(),或者对于隐式急切子类加载,请使用 Mapper.polymorphic_load 设置为 "selectin",而不是使用本节中描述的映射器级 mapper.with_polymorphic 参数。此参数调用旨在重写 SELECT 语句中 FROM 子句的复杂启发式方法,这些方法可能会干扰更复杂语句的构建,尤其是包含引用同一映射实体的嵌套子查询的语句。

例如,我们可以使用 Mapper.polymorphic_load 将我们的 Employee 映射设置为 "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

当使用映射器级“with polymorphic”时,查询也可以直接引用子类实体,它们隐式地表示多态查询中的连接表。在上面,我们可以自由地引用 ManagerEngineer,直接针对默认的 Employee 实体

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 实体或其子实体,我们将再次直接使用 with_polymorphic() 来定义这些别名实体,如 使用 with_polymorphic 进行别名 中所述。

为了更集中地控制多态可选择项,可以使用更传统的映射器级多态控制形式,即 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")可能更受欢迎,仅在需要时才在每个查询基础上使用 with_polymorphic()

连接到特定子类型或 with_polymorphic() 实体

由于 with_polymorphic() 实体是 aliased() 的一个特例,为了将多态实体作为连接的目标进行处理,特别是在使用 relationship() 结构作为 ON 子句时,我们使用与常规别名相同的技术,如 使用关系在别名目标之间连接 中所述,最简洁地使用 PropComparator.of_type()。在下面的示例中,我们说明了从父 Company 实体沿着一对多关系 Company.employees 进行的连接,该关系在 设置 中被配置为链接到 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() 目标的特定子类型。上面的查询可以严格地用 Engineer 目标写成如下

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

可以观察到,连接到 Engineer 目标直接,而不是“多态可选择项” with_polymorphic(Employee, [Engineer]),具有使用内部 JOIN 而不是 LEFT OUTER JOIN 的有用特性,这从 SQL 优化器的角度来看通常更有效。

多态子类型的急切加载

使用 PropComparator.of_type() 与上一节中使用 Select.join() 方法类似,也可以等效地应用于 关系加载选项,如 selectinload()joinedload()

例如,如果我们要加载 Company 对象,并使用 with_polymorphic() 结构针对整个层次结构急切加载 Company.employees 的所有元素,我们可以写

>>> 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() 应用于现有的急切加载

另请参阅

将 selectin_polymorphic() 应用于现有的急切加载 - 说明了上面使用 selectin_polymorphic() 的等效示例

单一继承映射的 SELECT 语句

单表继承设置

本节讨论单表继承,如 单表继承 所述,它使用单个表来表示层次结构中的多个类。

查看本节的 ORM 设置.

与连接继承映射相比,针对单表继承映射构造 SELECT 语句往往更简单,因为对于所有单表继承层次结构,只有一个表。

无论继承层次结构是全部单一继承还是混合了连接继承和单一继承,针对单一继承的 SELECT 语句通过使用额外的 WHERE 条件限制 SELECT 语句来区分针对基类的查询与针对子类的查询。

例如,针对 Employee 的单一继承示例映射的查询将使用简单 SELECT 语句从表中加载类型为 ManagerEngineerEmployee 的对象

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

优化单一继承的属性加载

单一继承映射关于如何选择子类属性的默认行为类似于连接继承,即子类特定的属性默认情况下仍然会发出第二个 SELECT。在下面的示例中,加载了一个类型为 Manager 的单个 Employee,但是由于请求的类是 Employee,因此 Manager.manager_name 属性默认情况下不存在,当访问该属性时会发出额外的 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 参数,并将设置设置为 "inline",对于那些预期经常加载其特定子类属性的子类来说。下面是说明 设置 的示例,它已修改为包含此选项

>>> 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",
...     }

使用上面的映射,ManagerEngineer 类将在针对 Employee 实体的 SELECT 语句中自动包含其列

>>> print(select(Employee))
SELECT employee.id, employee.name, employee.type, employee.manager_name, employee.engineer_info FROM employee

继承加载 API

对象名称 描述

selectin_polymorphic(base_cls, classes)

指示应针对所有特定于子类的属性进行急切加载。

with_polymorphic(base, classes[, selectable, flat, ...])

生成一个 AliasedClass 结构,该结构指定给定基类的后代映射的列。

function 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, aliased: bool = False, innerjoin: bool = False, adapt_on_names: bool = False, name: str | None = None, _use_mapper_path: bool = False) AliasedClass[_O]

生成一个 AliasedClass 结构,该结构指定给定基类的后代映射的列。

使用此方法将确保每个后代映射的表都包含在 FROM 子句中,并将允许对这些表使用 filter() 条件。生成的实例也将拥有那些已经加载的列,因此将不需要“后期获取”那些列。

另请参阅

使用 with_polymorphic() - 对 with_polymorphic() 的完整讨论。

参数:
  • base – 要别名化的基类。

  • classes – 单个类或映射器,或继承自基类的类/映射器列表。或者,它也可以是字符串 '*',在这种情况下,所有下降的映射类将被添加到 FROM 子句中。

  • aliased – 当为 True 时,可选择的将被别名化。对于 JOIN,这意味着 JOIN 将从子查询中 SELECT,除非 with_polymorphic.flat 标志被设置为 True,这对于更简单的用例来说是推荐的。

  • flat – 布尔值,将传递给 FromClause.alias() 调用,以便 Join 对象的别名将别名化 JOIN 中的各个表,而不是创建子查询。这通常受所有现代数据库在右嵌套 JOIN 方面的支持,并且通常会产生更有效的查询。只要生成的 SQL 是有效的,建议设置此标志。

  • selectable

    将用于代替生成的 FROM 子句的表或子查询。如果任何所需的类使用具体表继承,则此参数是必需的,因为 SQLAlchemy 目前无法自动在表之间生成 UNION。如果使用,则 selectable 参数必须表示每个映射类映射的完整表和列集。否则,未计入的映射列将导致其表直接附加到 FROM 子句中,这通常会导致错误的结果。

    当保留其默认值 False 时,分配给基映射器的多态可选择用于选择行。但是,它也可以作为 None 传递,这将绕过配置的多态可选择,而是为给定的目标类构建一个临时的可选择;对于连接表继承,这将是一个连接,其中包含所有目标映射器及其子类。

  • polymorphic_on – 要用作给定可选择的“鉴别器”列的列。如果没有给出,将使用基类的映射器(如果有)的 polymorphic_on 属性。这对于默认情况下没有多态加载行为的映射很有用。

  • innerjoin – 如果为 True,则将使用 INNER JOIN。这只能在仅查询一个特定子类型时指定

  • adapt_on_names

    aliased.adapt_on_names 参数传递给别名化对象。这可能在给定可选择与现有映射可选择无关的情况下的情况下有用。

    版本 1.4.33 中的新功能。

  • name

    赋予生成的 AliasedClass 的名称。

    版本 2.0.31 中的新功能。

function sqlalchemy.orm.selectin_polymorphic(base_cls: _EntityType[Any], classes: Iterable[Type[Any]]) _AbstractLoad

指示应针对所有特定于子类的属性进行急切加载。

这使用了一个额外的 SELECT 与 IN 相对所有匹配的主键值,并且是 mapper.polymorphic_load 参数上的 "selectin" 设置的每个查询模拟。

版本 1.2 中的新功能。