SqlAlchemy 2.0 中文文档(二十)(1)

简介: SqlAlchemy 2.0 中文文档(二十)


原文:docs.sqlalchemy.org/en/20/contents.html

用于查询的 ORM API 功能

原文:docs.sqlalchemy.org/en/20/orm/queryguide/api.html

ORM 加载选项

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

另请参阅

  • 列加载选项 - 详细说明了影响列和 SQL 表达式映射属性加载方式的映射器和加载选项
  • 关系加载技术 - 详细说明了影响relationship()映射属性加载方式的关系和加载选项

ORM 执行选项

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

ORM 级别的选项与 Core 级别的执行选项有所不同,Core 级别的执行选项记录在Connection.execution_options()。重要的是要注意,下面讨论的 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的另一个用例是支持各种属性加载功能,这些功能可以根据每个查询的情况改变如何加载属性。适用于此选项的选项包括:

  • with_expression()选项
  • PropComparator.and_() 方法可以修改加载器策略加载的内容
  • contains_eager() 选项
  • with_loader_criteria() 选项
  • load_only() 选项来选择要刷新的属性

populate_existing 执行选项相当于 1.x 风格 ORM 查询中的 Query.populate_existing() 方法。

请参见

我使用 Session 重新加载数据,但它没有看到我在其他地方提交的更改 - 在 常见问题解答

刷新 / 过期 - 在 ORM Session 文档中 ### 自动刷新

当作为 False 传递时,此选项将导致 Session 不调用 “自动刷新” 步骤。这相当于使用 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 执行选项相当于 1.x 风格 ORM 查询中的 Query.autoflush() 方法。

请参见

刷新 ### 使用 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对象上的Result.yield_per()方法,该方法建立了要提取的行的固定大小以及一次构造的 ORM 对象的相应限制。

提示

现在yield_per也作为 Core 执行选项可用,详细描述在使用服务器端游标(又名流式结果)。本节详细介绍了将yield_per作为 ORM Session的执行选项的用法。该选项在两种情况下的行为尽可能相似。

当与 ORM 一起使用时,yield_per必须通过给定语句上的Executable.execution_options()方法或通过将其传递给Session.execute()Session.execute.execution_options参数来建立,或者通过其他类似的Session方法,例如Session.scalars()。下面是获取 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对象中获取 ORM 行时,该对象使用Result.unique()过滤器,并同时使用yield_per执行选项时,会引发异常。

当在 1.x 风格 ORM 使用遗留Query对象时,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_map,将 None 映射到替代模式,我们可以将 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_tabletest_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 示例 部分。请参阅脚本 separate_schema_translates.py,了解如何使用完整的分片 API 进行上述用例的演示。

检查来自启用 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'>}]

当与非 ORM 对象一起使用 Select.column_descriptions 时,诸如普通 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 中进行了更改:当对非 ORM 启用的 Select 使用 Select.column_descriptions 属性时,现在会返回一个值。以前会引发 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 “selectable” 相同。例如,在连接表继承方案中,"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 一组在一个命名空间下由Query返回的 SQL 表达式。
join(left, right[, onclause, isouter, …]) 生成左右子句之间的内部连接。
outerjoin(left, right[, onclause, full]) 生成左外连接 left 和 right 子句之间的左外连接。
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)

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

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

另请参阅

ORM 实体别名 - 在 SQLAlchemy 统一教程 中

选择 ORM 别名 - 在 ORM 查询指南 中

参数:

  • element – 要别名化的元素。通常是一个映射类,但为了方便起见,也可以是一个FromClause 元素。
  • alias – 可选的可选择单元,用于将元素映射到。通常用于将对象链接到子查询,并且应该是一个别名选择构造,就像从 Query.subquery() 方法或 Select.subquery()Select.alias() 方法的 select() 构造产生的一样。
  • name – 用于别名的可选字符串名称,如果未由 alias 参数指定。名称,除其他外,形成了将通过 Query 对象返回的元组访问的属性名称。创建 Join 对象的别名时不支持。
  • flat – 布尔值,将传递给 FromClause.alias() 调用,以便 Join 对象的别名将别名加入联接内部的各个表,而不是创建子查询。这通常由所有现代数据库支持,关于右嵌套联接通常生成更有效的查询。
  • 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 的映射类的“别名”形式。

ORM 等效于 alias() 构造,此对象使用 __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()的目标。查看下面的链接以获取示例。

通常使用aliased()函数构造AliasedClass对象。当使用with_polymorphic()函数时,还会产生附加配置。

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

可以使用inspect()检查AliasedClass的基础Mapper、别名可选择和其他信息:

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

结果检查对象是AliasedInsp的一个实例。

另请参见

aliased()

with_polymorphic()

与别名类的关系

使用窗口函数限制行关系

类签名

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

class sqlalchemy.orm.util.AliasedInsp

AliasedClass对象提供检查接口。

给定一个 AliasedClass,使用 inspect() 函数将返回 AliasedInsp 对象:

from sqlalchemy import inspect
from sqlalchemy.orm import aliased
my_alias = aliased(MyMappedClass)
insp = inspect(my_alias)

AliasedInsp 的属性包括:

  • entity - 所代表的 AliasedClass
  • mapper - 映射底层类的 Mapper
  • selectable - 最终表示别名的 Alias 构造或 Select 构造。
  • name - 别名的名称。 在从 Query 中返回结果元组时,也用作属性名称。
  • with_polymorphic_mappers - 表示在 AliasedClass 的 select 构造中表达的所有那些映射器的集合。
  • polymorphic_on - 一个替代列或 SQL 表达式,将用作多态加载的“辨别器”。

另请参阅

运行时检查 API

类签名

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

一组由 Query 在一个命名空间下返回的 SQL 表达式。

Bundle 本质上允许列导向的 Query 对象返回的基于元组的结果嵌套。 它还可以通过简单的子类化进行扩展,其中主要的重写功能是如何返回表达式集,允许后处理以及自定义返回类型,而不涉及 ORM 标识映射的类。

另请参阅

属性捆绑

成员

init(), c, columns, create_row_processor(), is_aliased_class, is_bundle, is_clause_element, is_mapper, label(), single_entity

类签名

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

method __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 – bundle 的名称。
  • *exprs – 组成 bundle 的列或 SQL 表达式。
  • single_entity=False – 如果为 True,则此Bundle的行可以像映射实体一样在任何封闭元组之外作为“单个实体”返回。
attribute c: ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]

Bundle.columns的别名。

attribute columns: ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]

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

例如:

bn = Bundle("mybundle", MyClass.x, MyClass.y)
q = sess.query(bn).filter(bn.c.x == 5)

还支持 bundle 的嵌套:

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 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 is_aliased_class = False

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

attribute is_bundle = True

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

attribute is_clause_element = False

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

attribute is_mapper = False

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

method label(name)

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

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

上述示例中,“selectinload”对于User.addresses将将给定的过滤条件应用于 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()选项将在给定的语句中使用,并将自动传播到所有从此查询下降的关系加载。

给定的 criteria 参数是一个接受cls参数的lambda。给定的类将扩展为包括所有映射的子类,而且本身不必是一个映射的类。

提示

当与contains_eager()加载选项一起使用with_loader_criteria()选项时,重要的是要注意with_loader_criteria()仅影响确定渲染的 SQL 的查询部分,涉及 WHERE 和 FROM 子句。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”功能的概述,请参阅使用 Lambda 将显著提速到语句生成,该功能仅供高级用户使用。

参数:

  • entity_or_base - 映射类,或者一组特定映射类的超类,适用于规则的对象。
  • where_criteria -
    应用限制条件的核心 SQL 表达式。这也可以是一个接受目标类作为参数的“lambda:”或 Python 函数,当给定类是一个具有许多不同映射子类的基类时。
    注意
    为了支持 pickle,使用模块级 Python 函数生成 SQL 表达式,而不是 lambda 或固定的 SQL 表达式,后者往往不能 pickle 化。
  • 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() 本身之外,还具有大量的自动化功能。 使用 ORM 启用的 SELECT 语句显式使用 join() 涉及使用 Select.select_from() 方法,如下所示:

from sqlalchemy.orm import join
stmt = select(User).\
 select_from(join(User, Address, User.addresses)).\
 filter(Address.email_address=='foo@bar.com')

在现代的 SQLAlchemy 中,上述连接可以更简洁地编写为:

stmt = select(User).\
 join(User.addresses).\
 filter(Address.email_address=='foo@bar.com')

警告

直接使用 join() 可能无法正确地处理现代 ORM 选项,例如 with_loader_criteria()。 强烈建议在创建 ORM 连接时使用像 Select.join()Select.join_from() 这样的方法提供的惯用连接模式。

另请参阅

连接 - 在 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 版中的新内容。


SqlAlchemy 2.0 中文文档(二十)(2)https://developer.aliyun.com/article/1560348

相关文章
|
2天前
|
SQL 缓存 API
SqlAlchemy 2.0 中文文档(二十)(5)
SqlAlchemy 2.0 中文文档(二十)
9 1
|
2天前
|
SQL 存储 测试技术
SqlAlchemy 2.0 中文文档(二十)(3)
SqlAlchemy 2.0 中文文档(二十)
10 1
|
2天前
|
SQL 测试技术 API
SqlAlchemy 2.0 中文文档(二十)(4)
SqlAlchemy 2.0 中文文档(二十)
10 1
|
2天前
|
SQL 缓存 API
SqlAlchemy 2.0 中文文档(二十八)(4)
SqlAlchemy 2.0 中文文档(二十八)
11 1
|
2天前
|
SQL 测试技术 API
SqlAlchemy 2.0 中文文档(二十)(2)
SqlAlchemy 2.0 中文文档(二十)
9 0
|
2天前
|
存储 SQL API
SqlAlchemy 2.0 中文文档(二十四)(5)
SqlAlchemy 2.0 中文文档(二十四)
5 0
|
2天前
|
存储 SQL API
SqlAlchemy 2.0 中文文档(二十四)(4)
SqlAlchemy 2.0 中文文档(二十四)
7 0
|
2天前
|
SQL 关系型数据库 数据库连接
SqlAlchemy 2.0 中文文档(二十四)(3)
SqlAlchemy 2.0 中文文档(二十四)
8 0
|
2天前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(二十四)(2)
SqlAlchemy 2.0 中文文档(二十四)
8 0
|
2天前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(二十四)(1)
SqlAlchemy 2.0 中文文档(二十四)
8 0