ORM API 查询功能

ORM 加载程序选项

加载程序选项是对象,当传递给 Select.options() 方法,该方法属于 Select 对象或类似的 SQL 结构时,会影响列和关系属性的加载方式。大多数加载程序选项源于 Load 层次结构。有关使用加载程序选项的完整概述,请参阅下面的链接部分。

另请参阅

  • 列加载选项 - 详细介绍映射器和加载选项,这些选项会影响如何加载列和 SQL 表达式映射属性

  • 关系加载技术 - 详细介绍关系和加载选项,这些选项会影响如何加载 relationship() 映射属性

ORM 执行选项

ORM 级别的执行选项是关键字选项,可以使用 Session.execute.execution_options 参数与语句执行关联,该参数是 Session 方法(例如 Session.execute()Session.scalars())接受的字典参数,或者通过使用 Executable.execution_options() 方法直接与要调用的语句关联,该方法接受这些选项作为任意关键字参数。

ORM 级别的选项不同于在 Connection.execution_options() 中记录的 Core 级别的执行选项。重要的是要注意,下面讨论的 ORM 选项与 Core 级别的 Connection.execution_options()Engine.execution_options() 方法不兼容;在该级别,即使 EngineConnection 与正在使用的 Session 相关联,这些选项也会被忽略。

在本节中,将使用 Executable.execution_options() 方法样式来说明示例。

填充现有

populate_existing 执行选项确保,对于加载的所有行,Session 中对应的实例将被完全刷新 - 擦除对象中的所有现有数据(包括挂起的更改)并用从结果加载的数据进行替换。

示例用法如下

>>> stmt = select(User).execution_options(populate_existing=True)
>>> result = session.execute(stmt)
SELECT user_account.id, user_account.name, user_account.fullname FROM user_account ...

通常,ORM 对象只加载一次,如果它们在后续的结果行中与主键匹配,则该行不会应用于该对象。这样做既是为了保留对象上的挂起的未刷新更改,也是为了避免刷新已存在的数据的开销和复杂性。Session 假设了一个高度隔离的事务的默认工作模型,并且对于期望在事务中(在本地更改之外)发生数据更改的用例,这些用例将使用显式步骤(如本方法)进行处理。

使用 populate_existing,可以刷新与查询匹配的任何对象集,并且它还可以控制关系加载程序选项。例如,刷新实例并刷新相关的对象集

stmt = (
    select(User)
    .where(User.name.in_(names))
    .execution_options(populate_existing=True)
    .options(selectinload(User.addresses))
)
# will refresh all matching User objects as well as the related
# Address objects
users = session.execute(stmt).scalars().all()

populate_existing 的另一个用例是支持各种属性加载功能,这些功能可以改变属性在每个查询的基础上的加载方式。对此有用的选项包括

populate_existing 执行选项等同于 Query.populate_existing() 方法,该方法位于 1.x 样式 ORM 查询中。

自动刷新

当此选项作为 False 传递时,将导致 Session 不调用“autoflush”步骤。这等效于使用 Session.no_autoflush 上下文管理器来禁用自动刷新。

>>> stmt = select(User).execution_options(autoflush=False)
>>> session.execute(stmt)
SELECT user_account.id, user_account.name, user_account.fullname FROM user_account ...

此选项也将在启用 ORM 的 UpdateDelete 查询中生效。

autoflush 执行选项等效于 Query.autoflush() 方法在 1.x 风格 ORM 查询中。

另请参阅

刷新

使用 Yield Per 获取大型结果集

yield_per 执行选项是一个整数值,它将导致 Result 每次只缓冲有限数量的行或 ORM 对象,然后将数据提供给客户端。

通常,ORM 会立即获取 **所有** 行,为每个行构建 ORM 对象并将这些对象组合成一个单一的缓冲区,然后将此缓冲区传递给 Result 对象作为要返回行的来源。这种行为背后的理由是,为了在获取结果集中的每个对象时,允许诸如联接预加载、结果唯一化以及依赖于标识映射保持一致状态的一般结果处理逻辑等功能正常运行。

yield_per 选项的目的是改变这种行为,以便 ORM 结果集针对迭代非常大的结果集(例如 > 10K 行)进行优化,在这些结果集中,用户已经确定上述模式不适用。当使用 yield_per 时,ORM 将改为将 ORM 结果批处理成子集合,并从每个子集合中单独生成行,因为 Result 对象正在被迭代,因此 Python 解释器不需要声明非常大的内存区域,这既耗时又会导致内存使用过度。该选项影响数据库游标的使用方式以及 ORM 构建要传递给 Result 的行和对象的方式。

提示

从上面可以看出,Result 必须以可迭代的方式使用,也就是说,使用迭代,例如 for row in result 或使用部分行方法,例如 Result.fetchmany()Result.partitions()。调用 Result.all() 将失去使用 yield_per 的意义。

使用 yield_per 等效于同时使用 Connection.execution_options.stream_results 执行选项(如果支持,则选择使用后端使用的服务器端游标)以及 Result.yield_per() 方法,该方法在返回的 Result 对象上,该方法确定要获取的行数的固定大小以及对应于一次构建多少 ORM 对象的限制。

提示

yield_per 现在也可用作 Core 执行选项,在 使用服务器端游标(也称为流式结果) 中详细介绍。本节详细介绍了 yield_per 作为使用 ORM Session 的执行选项的使用。该选项在两种情况下尽可能地类似地工作。

当与 ORM 一起使用时,必须通过给定语句上的 Executable.execution_options() 方法或将其传递给 Session.execute.execution_options 参数的 Session.execute() 或其他类似的 Session 方法,例如 Session.scalars() 来建立 yield_per。获取 ORM 对象的典型用法如下所示

>>> stmt = select(User).execution_options(yield_per=10)
>>> for user_obj in session.scalars(stmt):
...     print(user_obj)
SELECT user_account.id, user_account.name, user_account.fullname FROM user_account [...] ()
User(id=1, name='spongebob', fullname='Spongebob Squarepants') User(id=2, name='sandy', fullname='Sandy Cheeks') ... >>> # ... rows continue ...

上面的代码等效于下面的示例,该示例结合使用 Connection.execution_options.stream_resultsConnection.execution_options.max_row_buffer Core 级别的执行选项以及 Result.yield_per() 方法的 Result

# equivalent code
>>> stmt = select(User).execution_options(stream_results=True, max_row_buffer=10)
>>> for user_obj in session.scalars(stmt).yield_per(10):
...     print(user_obj)
SELECT user_account.id, user_account.name, user_account.fullname FROM user_account [...] ()
User(id=1, name='spongebob', fullname='Spongebob Squarepants') User(id=2, name='sandy', fullname='Sandy Cheeks') ... >>> # ... rows continue ...

yield_per 也通常与 Result.partitions() 方法结合使用,该方法将以分组的分区迭代行。每个分区的尺寸默认为传递给 yield_per 的整数值,如以下示例所示

>>> stmt = select(User).execution_options(yield_per=10)
>>> for partition in session.scalars(stmt).partitions():
...     for user_obj in partition:
...         print(user_obj)
SELECT user_account.id, user_account.name, user_account.fullname FROM user_account [...] ()
User(id=1, name='spongebob', fullname='Spongebob Squarepants') User(id=2, name='sandy', fullname='Sandy Cheeks') ... >>> # ... rows continue ...

yield_per 执行选项 **与** “子查询”预加载 加载或 “联接”预加载 加载(当使用集合时) **不兼容**。它可能与 “选择在”预加载 兼容,前提是数据库驱动程序支持多个独立的游标。

此外,yield_per 执行选项与 Result.unique() 方法 **不兼容**;因为此方法依赖于存储所有行的完整身份集,它必然会失去使用 yield_per 的意义,即处理任意数量的行。

在版本 1.4.6 中更改: 当从使用 Result.unique() 筛选器的 Result 对象中获取 ORM 行时,同时使用 yield_per 执行选项,将引发异常。

当使用旧版 Query 对象与 1.x 风格 ORM 使用时,Query.yield_per() 方法的结果将与 yield_per 执行选项相同。

身份令牌

深层炼金术

此选项是一个高级功能,主要用于 水平分片 扩展。对于从不同的“分片”或分区加载具有相同主键的对象的典型情况,请考虑首先为每个分片使用单独的 Session 对象。

“身份令牌”是一个任意值,可以与 身份键 关联,用于加载新对象。此元素首先是为了支持执行每行“分片”的扩展,在这些扩展中,对象可以从特定数据库表的任意数量的副本中加载,但这些副本仍然具有重叠的主键值。“身份令牌”的主要使用者是 水平分片 扩展,它提供了一个用于在特定数据库表的多个“分片”中持久化对象的通用框架。

identity_token 执行选项可以在每个查询的基础上使用,以直接影响此令牌。直接使用它,可以将多个具有相同主键和源表的但具有不同“身份”的对象填充到 Session 中。

一个这样的例子是使用 架构名称的转换 功能将来自不同架构中的相同命名表的对象填充到 Session 中,该功能可以影响查询范围内架构的选择。给定一个映射为

from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column


class Base(DeclarativeBase):
    pass


class MyTable(Base):
    __tablename__ = "my_table"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]

上面类别的默认“架构”名称是 None,这意味着不会将架构限定符写入 SQL 语句。但是,如果我们使用 Connection.execution_options.schema_translate_mapNone 映射到备用架构,我们可以将 MyTable 的实例放置到两个不同的架构中

engine = create_engine(
    "postgresql+psycopg://scott:tiger@localhost/test",
)

with Session(
    engine.execution_options(schema_translate_map={None: "test_schema"})
) as sess:
    sess.add(MyTable(name="this is schema one"))
    sess.commit()

with Session(
    engine.execution_options(schema_translate_map={None: "test_schema_2"})
) as sess:
    sess.add(MyTable(name="this is schema two"))
    sess.commit()

上面两个代码块分别为 Session 对象关联不同的架构转换映射,并且 MyTable 的一个实例被持久化到 test_schema.my_table 以及 test_schema_2.my_table 中。

上面的 Session 对象是独立的。如果我们想在一个事务中持久化这两个对象,我们需要使用 水平分片 扩展来完成此操作。

但是,我们可以通过以下方式来说明在一个会话中查询这些对象

with Session(engine) as sess:
    obj1 = sess.scalar(
        select(MyTable)
        .where(MyTable.id == 1)
        .execution_options(
            schema_translate_map={None: "test_schema"},
            identity_token="test_schema",
        )
    )
    obj2 = sess.scalar(
        select(MyTable)
        .where(MyTable.id == 1)
        .execution_options(
            schema_translate_map={None: "test_schema_2"},
            identity_token="test_schema_2",
        )
    )

obj1obj2 彼此不同。但是,它们都引用了 MyTable 类的主键 ID 1,但它们是不同的。这就是 identity_token 发挥作用的地方,我们可以在检查每个对象时看到这一点,我们查看 InstanceState.key 以查看两个不同的身份令牌

>>> from sqlalchemy import inspect
>>> inspect(obj1).key
(<class '__main__.MyTable'>, (1,), 'test_schema')
>>> inspect(obj2).key
(<class '__main__.MyTable'>, (1,), 'test_schema_2')

当使用 水平分片 扩展时,上述逻辑会自动执行。

2.0.0rc1 版本新增: - 添加了 identity_token ORM 级别的执行选项。

另请参阅

水平分片 - 在 ORM 示例 部分。有关使用完整分片 API 演示上述用例,请参见脚本 separate_schema_translates.py

检查来自 ORM 启用的 SELECT 和 DML 语句的实体和列

select() 结构以及 insert()update()delete() 结构(对于后者的 DML 结构,截至 SQLAlchemy 1.4.33),都支持检查这些语句针对其创建的实体以及在结果集中返回的列和数据类型。

对于 Select 对象,此信息可从 Select.column_descriptions 属性获得。此属性的操作方式与旧版 Query.column_descriptions 属性相同。返回的格式是一个字典列表

>>> from pprint import pprint
>>> user_alias = aliased(User, name="user2")
>>> stmt = select(User, User.id, user_alias)
>>> pprint(stmt.column_descriptions)
[{'aliased': False,
  'entity': <class 'User'>,
  'expr': <class 'User'>,
  'name': 'User',
  'type': <class 'User'>},
 {'aliased': False,
  'entity': <class 'User'>,
  'expr': <....InstrumentedAttribute object at ...>,
  'name': 'id',
  'type': Integer()},
 {'aliased': True,
  'entity': <AliasedClass ...; User>,
  'expr': <AliasedClass ...; User>,
  'name': 'user2',
  'type': <class 'User'>}]

Select.column_descriptions 与非 ORM 对象(例如普通的 TableColumn 对象)一起使用时,条目将在所有情况下都包含有关返回的单个列的基本信息

>>> stmt = select(user_table, address_table.c.id)
>>> pprint(stmt.column_descriptions)
[{'expr': Column('id', Integer(), table=<user_account>, primary_key=True, nullable=False),
  'name': 'id',
  'type': Integer()},
 {'expr': Column('name', String(), table=<user_account>, nullable=False),
  'name': 'name',
  'type': String()},
 {'expr': Column('fullname', String(), table=<user_account>),
  'name': 'fullname',
  'type': String()},
 {'expr': Column('id', Integer(), table=<address>, primary_key=True, nullable=False),
  'name': 'id_1',
  'type': Integer()}]

1.4.33 版本变更: Select.column_descriptions 属性现在针对非 ORM 启用的 Select 返回一个值。以前,这将引发 NotImplementedError

对于 insert()update()delete() 结构,有两个单独的属性。一个是 UpdateBase.entity_description,它返回有关 DML 结构将影响的主要 ORM 实体和数据库表的信息

>>> from sqlalchemy import update
>>> stmt = update(User).values(name="somename").returning(User.id)
>>> pprint(stmt.entity_description)
{'entity': <class 'User'>,
 'expr': <class 'User'>,
 'name': 'User',
 'table': Table('user_account', ...),
 'type': <class 'User'>}

提示

UpdateBase.entity_description 包含一个条目 "table",它实际上是语句要插入、更新或删除的表,它并不总是与类可能映射到的 SQL“可选择项”相同。例如,在连接表继承方案中,"table" 将引用给定实体的本地表。

另一个是 UpdateBase.returning_column_descriptions,它以与 Select.column_descriptions 大致相同的方式提供有关 RETURNING 集合中存在的列的信息

>>> pprint(stmt.returning_column_descriptions)
[{'aliased': False,
  'entity': <class 'User'>,
  'expr': <sqlalchemy.orm.attributes.InstrumentedAttribute ...>,
  'name': 'id',
  'type': Integer()}]

1.4.33 版本新增: 添加了 UpdateBase.entity_descriptionUpdateBase.returning_column_descriptions 属性。

其他 ORM API 结构

对象名称 描述

aliased(element[, alias, name, flat, ...])

生成给定元素的别名,通常是 AliasedClass 实例。

AliasedClass

表示映射类的“别名”形式,用于 Query。

AliasedInsp

提供 AliasedClass 对象的检查接口。

Bundle

一个 SQL 表达式分组,这些表达式由 Query 在一个命名空间下返回。

join(left, right[, onclause, isouter, ...])

在左侧和右侧子句之间生成内连接。

outerjoin(left, right[, onclause, full])

在左侧和右侧子句之间生成左外连接。

with_loader_criteria(entity_or_base, where_criteria[, loader_only, include_aliases, ...])

为特定实体的所有出现添加额外的 WHERE 条件到加载中。

with_parent(instance, prop[, from_entity])

创建过滤条件,将此查询的主体与给定的关联实例相关联,使用已建立的 relationship() 配置。

function sqlalchemy.orm.aliased(element: _EntityType[_O] | FromClause, alias: FromClause | None = None, name: str | None = None, flat: bool = False, adapt_on_names: bool = False) AliasedClass[_O] | FromClause | AliasedType[_O]

生成给定元素的别名,通常是 AliasedClass 实例。

例如:

my_alias = aliased(MyClass)

stmt = select(MyClass, my_alias).filter(MyClass.id > my_alias.id)
result = session.execute(stmt)

The aliased() 函数用于创建映射类到新可选择的临时映射。默认情况下,可选择项是使用通常映射的可选择项(通常是 Table )生成的,使用 FromClause.alias() 方法。但是,aliased() 也可以用于将类链接到新的 select() 语句。此外,with_polymorphic() 函数是 aliased() 的变体,旨在指定一个所谓的“多态可选择项”,它对应于多个联接继承子类的并集。

为了方便起见,aliased() 函数还接受普通的 FromClause 结构,例如 Tableselect() 结构。在这些情况下,FromClause.alias() 方法将在对象上调用,并返回新的 Alias 对象。在这种情况下,返回的 Alias 未映射到 ORM。

参数:
  • element – 要设置别名的元素。通常是映射类,但为了方便起见,它也可以是 FromClause 元素。

  • alias – 可选的可选择项单元,用于将元素映射到该单元。这通常用于将对象链接到子查询,并且应该是从 Query.subquery() 方法或 Select.subquery()Select.alias() 方法(select() 结构)生成的别名选择结构。

  • name – 可选的字符串名称,用于别名,如果未由 alias 参数指定。该名称(除其他事项外)构成了可通过 Query 对象返回的元组访问的属性名称。不支持创建 Join 对象的别名。

  • flat

    布尔值,将传递给 FromClause.alias() 调用,以便 Join 对象的别名将别名设置为联接内的各个表,而不是创建子查询。这通常由所有现代数据库(关于右嵌套联接)支持,并且通常会产生更高效的查询。

    aliased.flataliased.name 结合使用时,生成的联接将使用类似于 <prefix>_<tablename> 的命名方案对各个表设置别名。此命名方案仅用于可见性/调试目的,并且特定方案可能会更改,恕不另行通知。

    2.0.32 版新增: 添加了对将 aliased.namealiased.flat 结合使用的支持。以前,这将引发 NotImplementedError

  • adapt_on_names

    如果为 True,当将 ORM 实体的映射列映射到给定可选择项的映射列时,将使用更宽松的“匹配” - 如果给定可选择项没有与实体上的列相对应的列,则将执行基于名称的匹配。这在将实体与某些派生可选择项(例如使用聚合函数的可选择项)关联时,是该方法的用例。

    class UnitPrice(Base):
        __tablename__ = 'unit_price'
        ...
        unit_id = Column(Integer)
        price = Column(Numeric)
    
    aggregated_unit_price = Session.query(
                                func.sum(UnitPrice.price).label('price')
                            ).group_by(UnitPrice.unit_id).subquery()
    
    aggregated_unit_price = aliased(UnitPrice,
                alias=aggregated_unit_price, adapt_on_names=True)

    上面,对 aggregated_unit_price 的函数,这些函数引用 .price 将返回 func.sum(UnitPrice.price).label('price') 列,因为它与名称“price”匹配。通常,“price”函数与实际的 UnitPrice.price 列没有“列对应关系”,因为它不是原始列的代理。

class sqlalchemy.orm.util.AliasedClass

表示映射类的“别名”形式,用于 Query。

这是 alias() 结构的 ORM 等效项,该对象使用 __getattr__ 方案模拟映射类,并维护对真实 Alias 对象的引用。

AliasedClass 的主要用途是在 ORM 生成的 SQL 语句中用作备用项,以便在多个上下文中使用现有的映射实体。一个简单的示例

# find all pairs of users with the same name
user_alias = aliased(User)
session.query(User, user_alias).\
                join((user_alias, User.id > user_alias.id)).\
                filter(User.name == user_alias.name)

AliasedClass 也能够将现有的映射类映射到一个全新的可选择对象,前提是该可选择对象与现有的映射可选择对象在列方面兼容,并且它也可以在映射中配置为 relationship() 的目标。有关示例,请参见下面的链接。

AliasedClass 对象通常使用 aliased() 函数构造。它在使用 with_polymorphic() 函数时也会以额外的配置方式生成。

生成的的对象是 AliasedClass 的实例。此对象实现了一个属性方案,该方案产生与原始映射类相同的属性和方法接口,允许 AliasedClass 与任何在原始类上工作的属性技术兼容,包括混合属性(参见 混合属性)。

AliasedClass 可以使用 inspect() 检查其底层的 Mapper、别名可选择对象和其他信息。

from sqlalchemy import inspect
my_alias = aliased(MyClass)
insp = inspect(my_alias)

生成的检查对象是 AliasedInsp 的实例。

类签名

class sqlalchemy.orm.AliasedClass (sqlalchemy.inspection.Inspectable, sqlalchemy.orm.ORMColumnsClauseRole)

class sqlalchemy.orm.util.AliasedInsp

提供 AliasedClass 对象的检查接口。

使用 inspect() 函数,在给定 AliasedClass 的情况下返回 AliasedInsp 对象。

from sqlalchemy import inspect
from sqlalchemy.orm import aliased

my_alias = aliased(MyMappedClass)
insp = inspect(my_alias)

AliasedInsp 上的属性包括

  • entity - 表示的 AliasedClass

  • mapper - 映射底层类的 Mapper

  • selectable - 最终表示别名 TableSelect 结构的 Alias 结构。

  • name - 别名的名称。在从 Query 返回的结果元组中,它也用作属性名称。

  • with_polymorphic_mappers - Mapper 对象的集合,指示在 AliasedClass 的选择结构中表达的所有映射器。

  • polymorphic_on - 将用作多态加载的“鉴别器”的备用列或 SQL 表达式。

另请参阅

运行时检查 API

类签名

class sqlalchemy.orm.AliasedInsp (sqlalchemy.orm.ORMEntityColumnsClauseRole, sqlalchemy.orm.ORMFromClauseRole, sqlalchemy.sql.cache_key.HasCacheKey, sqlalchemy.orm.base.InspectionAttr, sqlalchemy.util.langhelpers.MemoizedSlots, sqlalchemy.inspection.Inspectable, typing.Generic)

class sqlalchemy.orm.Bundle

一个 SQL 表达式分组,这些表达式由 Query 在一个命名空间下返回。

Bundle 本质上允许嵌套由基于列的 Query 对象返回的基于元组的结果。它也可以通过简单的子类化进行扩展,其中要覆盖的主要功能是表达式集的返回方式,允许后处理以及自定义返回类型,而无需涉及 ORM 身份映射类。

类签名

class sqlalchemy.orm.Bundle (sqlalchemy.orm.ORMColumnsClauseRole, sqlalchemy.sql.annotation.SupportsCloneAnnotations, sqlalchemy.sql.cache_key.MemoizedHasCacheKey, sqlalchemy.inspection.Inspectable, sqlalchemy.orm.base.InspectionAttr)

method sqlalchemy.orm.Bundle.__init__(name: str, *exprs: _ColumnExpressionArgument[Any], **kw: Any)

构造一个新的 Bundle

例如

bn = Bundle("mybundle", MyClass.x, MyClass.y)

for row in session.query(bn).filter(
        bn.c.x == 5).filter(bn.c.y == 4):
    print(row.mybundle.x, row.mybundle.y)
参数:
  • name – 捆绑包的名称。

  • *exprs – 构成包的列或 SQL 表达式。

  • single_entity=False – 如果为 True,则此 Bundle 的行可以以与映射实体相同的方式,作为任何封闭元组之外的“单个实体”返回。

attribute sqlalchemy.orm.Bundle.c: ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]

Bundle.columns 的别名。

attribute sqlalchemy.orm.Bundle.columns: ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]

Bundle 所引用的 SQL 表达式命名空间。

例如

bn = Bundle("mybundle", MyClass.x, MyClass.y)

q = sess.query(bn).filter(bn.c.x == 5)

也支持包的嵌套

b1 = Bundle("b1",
        Bundle('b2', MyClass.a, MyClass.b),
        Bundle('b3', MyClass.x, MyClass.y)
    )

q = sess.query(b1).filter(
    b1.c.b2.c.a == 5).filter(b1.c.b3.c.y == 9)

另请参阅

Bundle.c

method sqlalchemy.orm.Bundle.create_row_processor(query: Select[Any], procs: Sequence[Callable[[Row[Any]], Any]], labels: Sequence[str]) Callable[[Row[Any]], Any]

为此 Bundle 生成“行处理”函数。

子类可以覆盖它,以便在获取结果时提供自定义行为。该方法在查询执行时传递语句对象和一组“行处理”函数;这些处理函数在给定结果行时将返回单个属性值,然后可以将其改编为任何类型的返回数据结构。

以下示例说明了用简单的 Python 字典替换通常的 Row 返回结构

from sqlalchemy.orm import Bundle

class DictBundle(Bundle):
    def create_row_processor(self, query, procs, labels):
        'Override create_row_processor to return values as
        dictionaries'

        def proc(row):
            return dict(
                zip(labels, (proc(row) for proc in procs))
            )
        return proc

来自上述 Bundle 的结果将返回字典值

bn = DictBundle('mybundle', MyClass.data1, MyClass.data2)
for row in session.execute(select(bn)).where(bn.c.data1 == 'd1'):
    print(row.mybundle['data1'], row.mybundle['data2'])
attribute sqlalchemy.orm.Bundle.is_aliased_class = False

如果此对象是 AliasedClass 的实例,则为 True。

attribute sqlalchemy.orm.Bundle.is_bundle = True

如果此对象是 Bundle 的实例,则为 True。

attribute sqlalchemy.orm.Bundle.is_clause_element = False

如果此对象是 ClauseElement 的实例,则为 True。

attribute sqlalchemy.orm.Bundle.is_mapper = False

如果此对象是 Mapper 的实例,则为 True。

method sqlalchemy.orm.Bundle.label(name)

提供此 Bundle 的副本,传递一个新的标签。

attribute sqlalchemy.orm.Bundle.single_entity = False

如果为 True,则针对单个 Bundle 的查询将作为单个实体返回,而不是作为键控元组中的元素返回。

function sqlalchemy.orm.with_loader_criteria(entity_or_base: _EntityType[Any], where_criteria: _ColumnExpressionArgument[bool] | Callable[[Any], _ColumnExpressionArgument[bool]], loader_only: bool = False, include_aliases: bool = False, propagate_to_loaders: bool = True, track_closure_variables: bool = True) LoaderCriteriaOption

为特定实体的所有出现添加额外的 WHERE 条件到加载中。

版本 1.4 中新增。

with_loader_criteria() 选项旨在为查询中的特定类型的实体添加限制条件,**全局**,这意味着它将应用于实体在 SELECT 查询中出现的以及任何子查询、联接条件和关系加载中出现的,包括急切加载器和延迟加载器,无需在查询的任何特定部分指定它。渲染逻辑使用与单表继承相同的系统,以确保对表应用特定的鉴别器。

例如,使用 2.0 样式 查询,我们可以限制 User.addresses 集合的加载方式,无论使用哪种加载方式

from sqlalchemy.orm import with_loader_criteria

stmt = select(User).options(
    selectinload(User.addresses),
    with_loader_criteria(Address, Address.email_address != 'foo'))
)

上面,User.addresses 的“selectinload”将给定的过滤条件应用于 WHERE 子句。

另一个示例,其中过滤将应用于联接的 ON 子句,在本例中使用 1.x 样式 查询

q = session.query(User).outerjoin(User.addresses).options(
    with_loader_criteria(Address, Address.email_address != 'foo'))
)

with_loader_criteria() 的主要目的是在 SessionEvents.do_orm_execute() 事件处理程序中使用它,以确保所有特定实体的出现都以特定方式进行过滤,例如过滤访问控制角色。它也可以用于将条件应用于关系加载。在下面的示例中,我们可以对特定 Session 发出的所有查询应用一组规则

session = Session(bind=engine)

@event.listens_for("do_orm_execute", session)
def _add_filtering_criteria(execute_state):

    if (
        execute_state.is_select
        and not execute_state.is_column_load
        and not execute_state.is_relationship_load
    ):
        execute_state.statement = execute_state.statement.options(
            with_loader_criteria(
                SecurityRole,
                lambda cls: cls.role.in_(['some_role']),
                include_aliases=True
            )
        )

在上面的示例中,SessionEvents.do_orm_execute() 事件将拦截使用 Session 发出的所有查询。对于那些是 SELECT 语句并且不是属性或关系加载的查询,自定义 with_loader_criteria() 选项将添加到查询中。该 with_loader_criteria() 选项将用于给定的语句中,并将自动传播到从该查询派生的所有关系加载。

给定的条件参数是一个 lambda,它接受一个 cls 参数。给定的类将扩展到包括所有映射的子类,并且不需要本身是映射的类。

提示

当在 with_loader_criteria() 选项与 contains_eager() 加载器选项结合使用时,重要的是要注意 with_loader_criteria() 仅影响查询中决定 WHERE 和 FROM 子句中渲染的 SQL 的部分。该 contains_eager() 选项不会影响 SELECT 语句在列子句之外的渲染,因此不会与 with_loader_criteria() 选项有任何交互。但是,它们“工作”的方式是 contains_eager() 旨在与已经以某种方式从附加实体中进行选择的查询一起使用,而 with_loader_criteria() 可以应用它的附加条件。

在下面的示例中,假设映射关系为 A -> A.bs -> B,给定的 with_loader_criteria() 选项将影响 JOIN 的渲染方式

stmt = select(A).join(A.bs).options(
    contains_eager(A.bs),
    with_loader_criteria(B, B.flag == 1)
)

上面,给定的 with_loader_criteria() 选项将影响由 .join(A.bs) 指定的 JOIN 的 ON 子句,因此按预期应用。该 contains_eager() 选项的作用是在列子句中添加来自 B 的列

SELECT
    b.id, b.a_id, b.data, b.flag,
    a.id AS id_1,
    a.data AS data_1
FROM a JOIN b ON a.id = b.a_id AND b.flag = :flag_1

上面语句中 contains_eager() 选项的使用对 with_loader_criteria() 选项的行为没有影响。如果省略了 contains_eager() 选项,那么 SQL 将与 FROM 和 WHERE 子句相同,其中 with_loader_criteria() 继续将它的条件添加到 JOIN 的 ON 子句中。添加 contains_eager() 仅影响列子句,因为会添加针对 b 的额外列,然后由 ORM 使用这些列来生成 B 实例。

警告

在调用 with_loader_criteria() 中使用 lambda 仅针对**每个唯一类调用一次**。自定义函数不应在此 lambda 内调用。有关“lambda SQL”功能的概述,请参阅 使用 Lambdas 为语句生产增加显着的速度提升,该功能仅供高级用户使用。

参数:
  • entity_or_base – 映射的类,或作为特定映射类集合的超类的类,规则将应用于该类。

  • where_criteria

    应用限制条件的 Core SQL 表达式。这也可以是接受目标类作为参数的“lambda:”或 Python 函数,当给定的类是具有许多不同映射子类的基类时。

    注意

    为了支持腌制,请使用模块级 Python 函数来生成 SQL 表达式,而不是 lambda 或固定的 SQL 表达式,这些表达式往往不能腌制。

  • include_aliases – 如果为 True,则将规则应用于 aliased() 结构。

  • propagate_to_loaders

    默认为 True,应用于关系加载器(如延迟加载器)。这表示选项对象本身(包括 SQL 表达式)将与每个加载的实例一起传递。设置为 False 以防止对象被分配给单个实例。

    另请参阅

    ORM 查询事件 - 包含使用 with_loader_criteria() 的示例。

    添加全局 WHERE/ON 条件 - 关于如何将 with_loader_criteria()SessionEvents.do_orm_execute() 事件结合使用的基本示例。

  • track_closure_variables

    当为 False 时,lambda 表达式内的闭包变量不会用作任何缓存键的一部分。这允许在 lambda 表达式内使用更复杂的表达式,但要求 lambda 确保每次给定特定类时它都返回相同的 SQL。

    版本 1.4.0b2 中的新功能。

function sqlalchemy.orm.join(left: _FromClauseArgument, right: _FromClauseArgument, onclause: _OnClauseArgument | None = None, isouter: bool = False, full: bool = False) _ORMJoin

在左侧和右侧子句之间生成内连接。

join()join() 提供的核心连接接口的扩展,其中左右可选择项不仅可以是核心可选择项对象(如 Table),还可以是映射类或 AliasedClass 实例。“on” 子句可以是 SQL 表达式或引用已配置的 relationship() 的 ORM 映射属性。

join() 在现代用法中并不常见,因为它的功能被 Select.join()Query.join() 方法封装了起来。这些方法比 join() 本身提供了更多的自动化功能。直接使用 join() 与启用了 ORM 的 SELECT 语句涉及使用 Select.select_from() 方法,如下所示:

from sqlalchemy.orm import join
stmt = select(User).\
    select_from(join(User, Address, User.addresses)).\
    filter(Address.email_address=='[email protected]')

在现代 SQLAlchemy 中,上面的连接可以更简洁地写成:

stmt = select(User).\
        join(User.addresses).\
        filter(Address.email_address=='[email protected]')

警告

直接使用 join() 可能无法与现代 ORM 选项(如 with_loader_criteria())正常工作。强烈建议使用 Select.join()Select.join_from() 等方法提供的惯用连接模式来创建 ORM 连接。

另请参阅

连接 - 在 ORM 查询指南 中了解有关惯用 ORM 连接模式的背景信息

function sqlalchemy.orm.outerjoin(left: _FromClauseArgument, right: _FromClauseArgument, onclause: _OnClauseArgument | None = None, full: bool = False) _ORMJoin

在左侧和右侧子句之间生成左外连接。

这是 join() 函数的“外连接”版本,其行为相同,只是生成了一个 OUTER JOIN。有关其他使用详细信息,请参阅该函数的文档。

function sqlalchemy.orm.with_parent(instance: object, prop: attributes.QueryableAttribute[Any], from_entity: _EntityType[Any] | None = None) ColumnElement[bool]

创建过滤条件,将此查询的主体与给定的关联实例相关联,使用已建立的 relationship() 配置。

例如:

stmt = select(Address).where(with_parent(some_user, User.addresses))

渲染的 SQL 与从给定父级在该属性上触发延迟加载器时渲染的 SQL 相同,这意味着适当的状态将从 Python 中的父级对象获取,而无需在渲染的语句中渲染到父级表的连接。

给定属性还可以使用 PropComparator.of_type() 来指示条件的左侧

a1 = aliased(Address)
a2 = aliased(Address)
stmt = select(a1, a2).where(
    with_parent(u1, User.addresses.of_type(a2))
)

以上用法等同于使用 from_entity() 参数

a1 = aliased(Address)
a2 = aliased(Address)
stmt = select(a1, a2).where(
    with_parent(u1, User.addresses, from_entity=a2)
)
参数:
  • instance – 具有某些 relationship() 的实例。

  • property – 类绑定属性,它指示应使用实例中的哪个关系来协调父/子关系。

  • from_entity

    要考虑为左侧的实体。这默认为 Query 本身的“零”实体。

    版本 1.2 中的新功能。