关系加载技术¶
SQLAlchemy 的很大一部分是提供了对查询时如何加载相关对象的广泛控制。我们所说的“相关对象”是指使用 relationship()
在 mapper 上配置的集合或标量关联。
此行为可以在映射器构造时使用
relationship.lazy
参数添加到 relationship()
功能,以及将 ORM 加载器选项与 Select
构造一起使用。
关系的加载分为三类;延迟加载 /
预先加载,并且没有加载。延迟加载是指从查询返回的对象,最初没有加载相关对象。首次在特定对象上访问给定的集合或引用时,将发出额外的 SELECT 语句,以便加载请求的集合。
预先加载是指从查询返回的对象,其中已预先加载了相关集合或标量引用。ORM 通过增强它通常使用 JOIN 发出的 SELECT 语句以同时加载相关行,或者通过在主语句之后发出额外的 SELECT 语句以立即加载集合或标量引用来实现这一点。
“No” 加载是指在给定关系上禁用加载,要么该属性为空并且从未加载,要么在访问时引发错误,以防止不需要的延迟加载。
关系加载样式总结¶
关系加载的主要形式是:
延迟加载 - 通过lazy='select'
或lazyload()
提供 选项,这是在 attribute 访问时间来延迟加载单个 对象。 延迟加载是所有relationship()
结构,否则不会指示relationship.lazy
选项。 延迟加载在 延迟加载。
select IN 加载 - 可通过lazy='selectin'
或selectinload()
获得 选项,这种加载形式会发出第二个(或多个)SELECT 语句,该语句 将父对象的主键标识符组装成一个 IN 子句, 以便立即加载相关集合/标量引用的所有成员 按主键。 选择 IN 加载在 Select IN 加载中有详细说明。
联合加载 - 通过lazy='joined'
或joinedLoad()
提供 选项,则这种加载形式会将 JOIN 应用于给定的 SELECT 语句 ,以便将相关行加载到同一结果集中。 加入预先加载 在 Joined Eager Loading 中有详细说明。
RAISE LOADING - 可通过lazy='raise'
,lazy='raise_on_sql'
或raiseload()
选项获得,这种形式的加载是在通常发生惰性加载的同时触发的,除了它会引发 ORM 异常以防止应用程序进行不需要的惰性加载。Preventing unwanted lazy loading 中介绍了 raise loading。
子查询加载 - 可通过lazy='subquery'
或subqueryload()
获得 选项中,这种加载形式会发出第二个 SELECT 语句,该语句重新声明了 原始查询,然后将该子查询 JOIN 到 要加载的 related table to load all members of related collections / scalar 引用。 子查询预先加载在 子查询预先加载 中有详细说明。
只写加载 - 通过lazy='write_only'
提供,或者通过使用WriteOnlyMapped
注解。 此集合仅限 loader 样式会生成一个替代属性插桩,该 Instrumentation 永远不会 从数据库中隐式加载记录,而不是只允许WriteOnlyCollection.add()
中,则WriteOnlyCollection.add_all()
和WriteOnlyCollection.remove()
方法。 通过调用 SELECT 语句来查询集合 它是使用WriteOnlyCollection.select()
构造的 方法。 只写关系中讨论了只写加载。
动态加载 - 通过lazy='dynamic'
提供,或者通过使用DynamicMapped
注解。这是一种传统的仅限集合的加载器样式,当集合,从而允许针对集合的 内容。但是,动态加载器将隐式迭代底层 集合,这使得它们对管理 真正的大型收藏。动态加载器被 “write only” 集合,这将防止在任何情况下隐式加载底层集合。动态加载器在 Dynamic Relationship Loaders 中讨论。
在 Map 时配置 Loader 策略¶
可以在映射时将特定关系的加载器策略配置为在加载 mapped 类型的对象的所有情况下发生,而无需任何修改该对象的查询级选项。这是使用 relationship.lazy
参数配置的
relationship()
;此参数的常见值包括 Select
、SelectIn
和 Joined
。
下面的示例说明了
一对多,配置 Parent.children
关系以在发出 Parent
对象的 SELECT 语句时使用 Select IN 加载:
from typing import List
from sqlalchemy import ForeignKey
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
class Base(DeclarativeBase):
pass
class Parent(Base):
__tablename__ = "parent"
id: Mapped[int] = mapped_column(primary_key=True)
children: Mapped[List["Child"]] = relationship(lazy="selectin")
class Child(Base):
__tablename__ = "child"
id: Mapped[int] = mapped_column(primary_key=True)
parent_id: Mapped[int] = mapped_column(ForeignKey("parent.id"))
在上面,每当加载 Parent
对象的集合时,每个
Parent
还将使用发出第二个查询的 “selectin”
加载器策略填充其 children
集合。
relationship.lazy
参数的默认值为
“select”
,表示延迟加载。
使用 Loader 选项加载关系¶
另一种,也可能是更常见的配置加载策略的方法
是针对特定属性在每个查询的基础上使用
Select.options()
方法。 非常详细
可以使用 loader 选项控制关系加载;
最常见的是
joinedload()
中,selectInload()
和 lazyload()
一起。该选项接受一个类绑定属性,该属性引用应作为目标的特定类/属性:
from sqlalchemy import select
from sqlalchemy.orm import lazyload
# set children to load lazily
stmt = select(Parent).options(lazyload(Parent.children))
from sqlalchemy.orm import joinedload
# set children to load eagerly with a join
stmt = select(Parent).options(joinedload(Parent.children))
loader 选项也可以使用方法链接来 “链接”
要指定加载应如何进行更深的级别:
from sqlalchemy import select
from sqlalchemy.orm import joinedload
stmt = select(Parent).options(
joinedload(Parent.children).subqueryload(Child.subelements)
)
Chained loader 选项可以应用于 “lazy” 加载的集合。这意味着,当集合或关联在访问时延迟加载时,指定的选项将生效:
from sqlalchemy import select
from sqlalchemy.orm import lazyload
stmt = select(Parent).options(lazyload(Parent.children).subqueryload(Child.subelements))
在上面,查询将返回 Parent
对象,而不返回 children
已加载的集合。 当 children
集合位于特定的
首先访问 Parent
对象,它将延迟加载相关对象,但另外还会对子元素
应用 Eager Load
子项
的每个成员的集合。
向 loader 选项添加 Criteria¶
用于指示加载程序选项的关系属性包括向创建的联接的 ON 子句或所涉及的 WHERE 条件添加其他筛选条件的能力,具体取决于加载程序策略。这可以使用 PropComparator.and_()
来实现
方法,该方法将传递一个选项,以便限制加载的结果
添加到给定的筛选条件中:
from sqlalchemy import select
from sqlalchemy.orm import lazyload
stmt = select(A).options(lazyload(A.bs.and_(B.id > 5)))
使用限制条件时,如果已加载特定集合,则不会刷新该集合;要确保执行新标准,请应用 Populate Existing execution 选项:
from sqlalchemy import select
from sqlalchemy.orm import lazyload
stmt = (
select(A)
.options(lazyload(A.bs.and_(B.id > 5)))
.execution_options(populate_existing=True)
)
要向整个查询中出现的实体添加筛选条件,无论加载程序策略如何,也不管它在加载过程中出现的位置如何,请参阅 with_loader_criteria()
函数。
在 1.4 版本加入.
使用 Load.options() 指定子选项¶
使用方法链接,明确声明路径中每个链接的加载器样式。要在不更改特定属性的现有加载器样式的情况下沿路径导航,可以使用 defaultload()
方法/函数:
from sqlalchemy import select
from sqlalchemy.orm import defaultload
stmt = select(A).options(defaultload(A.atob).joinedload(B.btoc))
类似的方法可用于使用 Load.options()
方法一次指定多个子选项:
from sqlalchemy import select
from sqlalchemy.orm import defaultload
from sqlalchemy.orm import joinedload
stmt = select(A).options(
defaultload(A.atob).options(joinedload(B.btoc), joinedload(B.btod))
)
另请参阅
在相关对象和集合上使用 load_only() - 说明了组合关系和面向列的加载器选项的示例。
注意
应用于对象的延迟加载集合的加载器选项对特定对象实例是“粘性的”,这意味着只要该对象存在于内存中,它们就会在该特定对象加载的集合上持续存在。例如,给定前面的示例:
stmt = select(Parent).options(lazyload(Parent.children).subqueryload(Child.subelements))
如果上述查询加载的特定 Parent
对象上的 children
集合过期(例如,当 Session
对象的事务被提交或回滚时,或者使用 Session.expire_all()
时),当下次访问 Parent.children
集合以重新加载它时,Child.subelements
collection 将再次使用 subquery Eager loading 加载。即使上述 Parent
对象从指定一组不同的
选项。更改现有对象上的选项而不将其清除
并重新加载,则必须使用
Populate Existing execution 选项:
# change the options on Parent objects that were already loaded
stmt = (
select(Parent)
.execution_options(populate_existing=True)
.options(lazyload(Parent.children).lazyload(Child.subelements))
.all()
)
如果上面加载的对象已从 Session
中完全清除,例如由于垃圾回收或 Session.expunge_all()
,则 “sticky” 选项也将消失,新创建的
如果再次加载,对象将使用新选项。
未来的 SQLAlchemy 版本可能会添加更多替代方案来作已加载对象的加载器选项。
延迟加载¶
默认情况下,所有对象间关系都是延迟加载的。与 relationship()
关联的标量或集合属性
包含一个触发器,该触发器在首次访问属性时触发。 这
trigger 通常在访问点发出 SQL 调用
要加载相关对象或对象:
>>> spongebob.addresses
SELECT
addresses.id AS addresses_id,
addresses.email_address AS addresses_email_address,
addresses.user_id AS addresses_user_id
FROM addresses
WHERE ? = addresses.user_id
[5]
[<Address(u'spongebob@google.com')>, <Address(u'j25@yahoo.com')>]
不发出 SQL 的一种情况是简单的多对一关系,当相关对象可以单独通过其主键标识时,并且该对象已经存在于当前 Session
中。因此,虽然延迟加载对于相关集合来说可能很昂贵,但在使用简单的多对一针对相对较小的可能目标对象集加载大量对象的情况下,延迟加载可能能够在本地引用这些对象,而不会发出与父对象一样多的 SELECT 语句。
这种“属性访问时加载”的默认行为称为“延迟”或“选择”加载 - 名称为“select”,因为通常在首次访问属性时发出“SELECT”语句。
可以为给定的属性启用延迟加载,该属性通常使用 lazyload()
加载器选项以其他方式配置:
from sqlalchemy import select
from sqlalchemy.orm import lazyload
# force lazy loading for an attribute that is set to
# load some other way normally
stmt = select(User).options(lazyload(User.addresses))
使用 raiseload 防止不需要的延迟负载¶
lazyload()
策略产生的效果是
对象关系映射中提到的常见问题;这
N 加 1 个问题,该问题指出,对于加载的任意 N 个对象,
访问它们的 lazy-loaded 属性意味着将有 N+1 个 SELECT
statements 发出。 在 SQLAlchemy 中,N+1 问题的常用缓解措施
是利用其非常强大的 EAGER Load 系统。 但是,预先加载
要求使用
选择
up front(前面)。代码可能访问其他未预先加载的属性的问题,其中不需要延迟加载,可以使用 raiseload()
策略来解决;此加载器策略将延迟加载的行为替换为引发的信息性错误:
from sqlalchemy import select
from sqlalchemy.orm import raiseload
stmt = select(User).options(raiseload(User.addresses))
在上面,从上述查询加载的 User
对象不会加载 .addresses
集合;如果某些代码稍后尝试访问此属性,则会引发 ORM 异常。
raiseload()
可以与所谓的 “wildcard” 说明符一起使用,以指示所有关系都应该使用此策略。例如,要只将一个 attribute 设置为 eager loading,其余所有 attribute 都设置为 raise:
from sqlalchemy import select
from sqlalchemy.orm import joinedload
from sqlalchemy.orm import raiseload
stmt = select(Order).options(joinedload(Order.items), raiseload("*"))
上述通配符将适用于所有关系,而不仅仅是 Order
除了 items
,但也包括 Item
对象上的所有 API。 设置
raiseload()
中,使用
Load
指定完整路径:
from sqlalchemy import select
from sqlalchemy.orm import joinedload
from sqlalchemy.orm import Load
stmt = select(Order).options(joinedload(Order.items), Load(Order).raiseload("*"))
相反,要仅为 Item
对象设置 raise:
stmt = select(Order).options(joinedload(Order.items).raiseload("*"))
raiseload()
选项仅适用于关系属性。对于面向列的属性,defer()
选项支持
defer.raiseload
选项,其工作方式相同。
提示
“raiseload” 策略不适用
在 Unit of Work 刷新进程中。 这意味着,如果
Session.flush()
进程需要加载一个集合才能完成其工作,它将在绕过任何 raiseload()
的同时执行此作
指令。
加入 Eager Loading¶
Joined eager loading 是 SQLAlchemy ORM 中包含的最古老的预先加载样式。它的工作原理是将 JOIN(默认为 LEFT OUTER 连接)连接到发出的 SELECT 语句,并从与父级相同的结果集中填充目标标量/集合。
在映射级别,这看起来像:
class Address(Base):
# ...
user: Mapped[User] = relationship(lazy="joined")
联接预先加载通常作为查询的选项应用,而不是作为映射上的默认加载选项,特别是当用于集合而不是多对一引用时。这是使用 joinedload()
加载器选项实现的:
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import joinedload
>>> stmt = select(User).options(joinedload(User.addresses)).filter_by(name="spongebob")
>>> spongebob = session.scalars(stmt).unique().all()
SELECT
addresses_1.id AS addresses_1_id,
addresses_1.email_address AS addresses_1_email_address,
addresses_1.user_id AS addresses_1_user_id,
users.id AS users_id, users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
LEFT OUTER JOIN addresses AS addresses_1
ON users.id = addresses_1.user_id
WHERE users.name = ?
['spongebob']
提示
当在引用一对多或多对多集合时包含 joinedload()
时,必须将 Result.unique()
方法应用于返回的结果,这将按主键统一传入的行,否则将乘以连接。如果不存在,ORM 将引发错误。
这在现代 SQLAlchemy 中不是自动的,因为它改变了结果集的行为,以返回比语句通常返回的行数更少的 ORM 对象。因此,SQLAlchemy 保持了 Result.unique()
的显式使用,因此返回的对象在主键上被 uniqization 是不存在的歧义。
默认情况下发出的 JOIN 是 LEFT OUTER JOIN,以允许不引用相关行的 lead 对象。对于保证具有元素的属性,例如对引用外键为 NOT NULL 的相关对象的多对一引用,可以使用内部联接提高查询效率;这可以通过 relationship.innerJoin
标志在 Map 级别获得:
class Address(Base):
# ...
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
user: Mapped[User] = relationship(lazy="joined", innerjoin=True)
在查询选项级别,通过 joinedload.innerjoin
标志:
from sqlalchemy import select
from sqlalchemy.orm import joinedload
stmt = select(Address).options(joinedload(Address.user, innerjoin=True))
当 JOIN 应用于包含 OUTER JOIN 的链中时,JOIN 将自身右嵌套:
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import joinedload
>>> stmt = select(User).options(
... joinedload(User.addresses).joinedload(Address.widgets, innerjoin=True)
... )
>>> results = session.scalars(stmt).unique().all()
SELECT
widgets_1.id AS widgets_1_id,
widgets_1.name AS widgets_1_name,
addresses_1.id AS addresses_1_id,
addresses_1.email_address AS addresses_1_email_address,
addresses_1.user_id AS addresses_1_user_id,
users.id AS users_id, users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
LEFT OUTER JOIN (
addresses AS addresses_1 JOIN widgets AS widgets_1 ON
addresses_1.widget_id = widgets_1.id
) ON users.id = addresses_1.user_id
提示
如果在发出 SELECT 时使用数据库行锁定技术,这意味着 Select.with_for_update()
方法被用于发出 SELECT..FOR UPDATE 时,连接的表也可能被锁定,具体取决于正在使用的后端的行为。不建议在使用 SELECT 的同时使用 join 预先加载。FOR UPDATE 的请求。
Joined Eager Loading 的 Zen¶
由于 join eager loading 似乎与使用
Select.join()
时,它经常会产生关于何时以及如何应该使用的混淆
被使用。 了解两者之间的区别至关重要,虽然
Select.join()
用于更改查询结果,joinedload()
不费吹灰之力不改变查询的结果,而是隐藏渲染的 Join 的效果,只允许存在相关对象。
加载器策略背后的理念是,任何一组加载方案都可以应用于特定的查询,并且结果不会改变 - 只有
完全加载相关对象和集合所需的 SQL 语句数
变化。特定查询可能开始使用所有延迟加载。 使用后
在上下文中,可能会揭示特定属性或集合
始终被访问,并且更改 loader 会更有效
策略。 该策略可以在没有其他修改的情况下更改
)中,结果将保持相同,但较少的 SQL 语句会
被发出。在理论上(几乎在实践中),你不能对
Select
将使其根据加载器策略的更改加载一组不同的主对象或相关对象。
特别是 joinedload()
实现不影响以任何方式返回的实体行的结果的方式是,它会创建它添加到查询中的联接的匿名别名,以便查询的其他部分无法引用它们。例如,下面的查询使用 joinedload()
创建从用户
到地址
的 LEFT OUTER JOIN,但是针对Address.email_address
添加的 ORDER BY
无效 - Address
实体未在查询中命名:
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import joinedload
>>> stmt = (
... select(User)
... .options(joinedload(User.addresses))
... .filter(User.name == "spongebob")
... .order_by(Address.email_address)
... )
>>> result = session.scalars(stmt).unique().all()
SELECT
addresses_1.id AS addresses_1_id,
addresses_1.email_address AS addresses_1_email_address,
addresses_1.user_id AS addresses_1_user_id,
users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
LEFT OUTER JOIN addresses AS addresses_1
ON users.id = addresses_1.user_id
WHERE users.name = ?
ORDER BY addresses.email_address <-- this part is wrong !
['spongebob']
上述无效, ORDER BY addresses.email_address
因为 addresses
不在 FROM 列表中。加载 User
records 并按电子邮件地址排序的正确方法是使用 Select.join():
>>> from sqlalchemy import select
>>> stmt = (
... select(User)
... .join(User.addresses)
... .filter(User.name == "spongebob")
... .order_by(Address.email_address)
... )
>>> result = session.scalars(stmt).unique().all()
SELECT
users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
JOIN addresses ON users.id = addresses.user_id
WHERE users.name = ?
ORDER BY addresses.email_address
['spongebob']
上面的语句当然与前一个不同,因为 addresses
中的列根本不包含在结果中。 我们可以添加
joinedload()
返回,这样就有两个连接 - 一个是我们
正在排序,另一个用于匿名加载
User.addresses
集合:
>>> stmt = (
... select(User)
... .join(User.addresses)
... .options(joinedload(User.addresses))
... .filter(User.name == "spongebob")
... .order_by(Address.email_address)
... )
>>> result = session.scalars(stmt).unique().all()
SELECT
addresses_1.id AS addresses_1_id,
addresses_1.email_address AS addresses_1_email_address,
addresses_1.user_id AS addresses_1_user_id,
users.id AS users_id, users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users JOIN addresses
ON users.id = addresses.user_id
LEFT OUTER JOIN addresses AS addresses_1
ON users.id = addresses_1.user_id
WHERE users.name = ?
ORDER BY addresses.email_address
['spongebob']
我们在上面看到的是,我们对 Select.join()
的用法是提供 JOIN
子句,而我们对
joinedload()
只关心
User.addresses
集合中,对于结果中的每个 User
。在这种情况下,
这两个联接很可能看起来是多余的 - 它们确实是多余的。 如果我们想要
只使用一个 JOIN 进行集合加载和排序,我们使用
contains_eager()
选项,如下面的 Routing Explicit Joins/Statements into Eagerly Loaded Collections 中所述。但是要了解为什么 joinedload()
会这样做,请考虑一下我们是否
对特定地址
进行筛选:
>>> stmt = (
... select(User)
... .join(User.addresses)
... .options(joinedload(User.addresses))
... .filter(User.name == "spongebob")
... .filter(Address.email_address == "someaddress@foo.com")
... )
>>> result = session.scalars(stmt).unique().all()
SELECT
addresses_1.id AS addresses_1_id,
addresses_1.email_address AS addresses_1_email_address,
addresses_1.user_id AS addresses_1_user_id,
users.id AS users_id, users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users JOIN addresses
ON users.id = addresses.user_id
LEFT OUTER JOIN addresses AS addresses_1
ON users.id = addresses_1.user_id
WHERE users.name = ? AND addresses.email_address = ?
['spongebob', 'someaddress@foo.com']
在上面,我们可以看到这两个 JOIN 的角色非常不同。一个将恰好匹配一行,即 User
和 Address
的联接,其中
Address.email_address=='someaddress@foo.com'
。另一个 LEFT OUTER JOIN 将匹配与 User
相关的所有Address
行,并且仅用于填充返回的 User
对象的 User.addresses
集合。
通过将 joinedload()
的用法更改为另一种加载方式,我们可以更改集合的加载方式,完全独立于用于检索我们想要的实际 User
行的 SQL。下面我们更改 joinedload()
放入 selectInload()
中:
>>> stmt = (
... select(User)
... .join(User.addresses)
... .options(selectinload(User.addresses))
... .filter(User.name == "spongebob")
... .filter(Address.email_address == "someaddress@foo.com")
... )
>>> result = session.scalars(stmt).all()
SELECT
users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
JOIN addresses ON users.id = addresses.user_id
WHERE
users.name = ?
AND addresses.email_address = ?
['spongebob', 'someaddress@foo.com']
# ... selectinload() emits a SELECT in order
# to load all address records ...
使用联接预先加载时,如果查询包含影响联接外部返回的行的修饰符,例如使用 DISTINCT、LIMIT、OFFSET 或等效项时,则首先将完成的语句包装在子查询中,并且专门用于联接预先加载的联接将应用于子查询。SQLAlchemy 的 join eager loading 更进一步,然后更进一步,以绝对确保它不会影响查询的最终结果,只影响集合和相关对象的加载方式,无论查询的格式是什么。
另请参阅
选择 IN 加载¶
在大多数情况下,selectin 加载是预先加载对象集合的最简单、最有效的方法。selectin 预先加载不可行的唯一情况是,当模型使用复合主键,并且后端数据库不支持带 IN 的元组(当前包括 SQL Server)时。
使用 “selectin”
参数提供 “select IN” 预先加载,以
relationship.lazy
或使用 selectInload()
加载器选项。这种加载样式会发出一个 SELECT,该 SELECT 引用父对象的主键值,或者在与子对象的主键值存在多对一关系的情况下,在 IN 子句内,以便加载相关关联:
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import selectinload
>>> stmt = (
... select(User)
... .options(selectinload(User.addresses))
... .filter(or_(User.name == "spongebob", User.name == "ed"))
... )
>>> result = session.scalars(stmt).all()
SELECT
users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
WHERE users.name = ? OR users.name = ?
('spongebob', 'ed')
SELECT
addresses.id AS addresses_id,
addresses.email_address AS addresses_email_address,
addresses.user_id AS addresses_user_id
FROM addresses
WHERE addresses.user_id IN (?, ?)
(5, 7)
上面,第二个 SELECT 指的是 addresses.user_id IN (5, 7),
其中 “5” 和 “7” 是前两个 User
的主键值
加载的对象;在一批对象完全加载后,它们的 primary
键值被注入到第二个 SELECT 的 IN
子句中。由于 User
和 Address
之间的关系具有简单的主联接条件,并且提供 User
的主键值可以从 Address.user_id
派生,因此该语句根本没有联接或子查询。
对于简单的多对一加载,也不需要 JOIN,因为使用了父对象的外键值:
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import selectinload
>>> stmt = select(Address).options(selectinload(Address.user))
>>> result = session.scalars(stmt).all()
SELECT
addresses.id AS addresses_id,
addresses.email_address AS addresses_email_address,
addresses.user_id AS addresses_user_id
FROM addresses
SELECT
users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
WHERE users.id IN (?, ?)
(1, 2)
提示
我们所说的 “简单” 是指 relationship.primaryjoin
condition 表示
“one” 端和 “many” 端的直接外键,没有任何
附加标准。
Select IN 加载还支持多对多关系,目前它将在所有三个表中执行 JOIN作,以匹配从一侧到另一侧的行。
有关此类加载的注意事项包括:
该策略一次为最多 500 个父主键值发出 SELECT,因为主键在 SQL 语句中呈现为大型 IN 表达式。某些数据库(如 Oracle Database)对 IN 表达式的大小有硬性限制,总体而言,SQL 字符串的大小不应任意大。
由于 “selectin” 加载依赖于 IN,因此对于具有复合主键的映射,它必须使用 IN 的 “元组” 形式,如下所示WHERE (table.column_a, table.column_b) IN ((?, ?), (?, ?), (?, ?))
。SQL Server 当前不支持此语法,对于 SQLite,至少需要版本 3.15。SQLAlchemy 中没有特殊的逻辑来提前检查哪些平台支持此语法;如果针对不支持的平台运行,数据库将立即返回错误。SQLAlchemy 只是运行 SQL 使其失败的一个优点是,如果特定数据库确实开始支持此语法,它将在不对 SQLAlchemy 进行任何更改的情况下工作(就像 SQLite 一样)。
子查询 Eager Loading¶
旧版功能
subqueryload()
急切加载器在这一点上大多是遗留的,被 selectinload()
策略取代
它的设计更简单,更灵活,具有以下功能
Yield Per 的 SQL 语句,并且在大多数情况下会发出更高效的 SQL 语句。由于 subqueryload()
依赖于重新解释原始 SELECT 语句,因此当给定非常复杂的源查询时,它可能无法有效地工作。
subqueryload()
对于使用复合主键的对象预先加载集合的特定情况可能仍然有用,在 Microsoft SQL Server 后端上仍然不支持“tuple IN”语法。
子查询加载在作上与 selectin 预先加载类似,但发出的 SELECT 语句是从原始语句派生而来的,并且具有比 selectin 预先加载更复杂的查询结构。
子查询预先加载是使用 “subquery”
参数提供的,以
relationship.lazy
或使用 subqueryload()
加载器选项。
子查询预先加载的作是一次跨所有结果对象为每个要加载的关系发出第二个 SELECT 语句。此 SELECT 语句引用原始 SELECT 语句,包装在子查询中,以便我们为返回的主对象检索相同的主键列表,然后将其链接到所有集合成员的总和以一次加载它们:
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import subqueryload
>>> stmt = select(User).options(subqueryload(User.addresses)).filter_by(name="spongebob")
>>> results = session.scalars(stmt).all()
SELECT
users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
WHERE users.name = ?
('spongebob',)
SELECT
addresses.id AS addresses_id,
addresses.email_address AS addresses_email_address,
addresses.user_id AS addresses_user_id,
anon_1.users_id AS anon_1_users_id
FROM (
SELECT users.id AS users_id
FROM users
WHERE users.name = ?) AS anon_1
JOIN addresses ON anon_1.users_id = addresses.user_id
ORDER BY anon_1.users_id, addresses.id
('spongebob',)
有关此类加载的注意事项包括:
与 “selectin” 不同,“subquery” 加载器策略发出的 SELECT 语句需要一个子查询,并将继承原始查询中存在的任何性能限制。子查询本身也可能根据正在使用的数据库的具体情况产生性能损失。
“subquery” 加载会施加一些特殊的排序要求才能正常工作。将subqueryload()
与限制修饰符(如Select.limit()
或Select.offset()
)结合使用的查询应始终包含Select.order_by()
针对唯一列(例如主键),以便其他查询 由subqueryload()
发出的 Sequences 包含与父查询使用的 Sequences 相同的 Sequences。没有它,内部查询可能会返回错误的行:# incorrect, no ORDER BY stmt = select(User).options(subqueryload(User.addresses).limit(1)) # incorrect if User.name is not unique stmt = select(User).options(subqueryload(User.addresses)).order_by(User.name).limit(1) # correct stmt = ( select(User) .options(subqueryload(User.addresses)) .order_by(User.name, User.id) .limit(1) )
当用于多级深度的 EAGER 加载时,“subquery” 加载还会产生额外的性能/复杂性问题,因为子查询将被重复嵌套。
“subquery” 加载与 Yield Per 提供的 “batched” 加载不兼容,无论是对于集合关系还是标量关系。
由于上述原因,“selectin” 策略应该优先于 “subquery”。
另请参阅
使用哪种负载 ?¶
使用哪种类型的加载通常归结为优化 SQL 执行次数、发出的 SQL 的复杂性和获取的数据量之间的权衡。
一对多 / 多对多集合 - selectinload()
通常是最好的加载策略。它会发出一个额外的 SELECT,该 SELECT 使用尽可能少的表,使原始语句不受影响,并且对于任何类型的原始查询都是最灵活的。它唯一的主要限制是在不支持“元组 IN”的后端使用具有复合主键的表时,该后端当前包括 SQL Server 和非常旧的 SQLite 版本;所有其他包含的后端都支持它。
多对一 - joinedload()
策略是最通用的策略。在特殊情况下,如果存在非常少量的潜在相关值,immediateload()
策略也可能很有用,因为此策略将从本地 Session
中获取对象
如果相关对象已存在,则不发出任何 SQL。
多态 Eager Loading¶
支持基于每个 eager-load 的多态选项指定。请参阅 多态子类型的预先加载 部分,了解 PropComparator.of_type()
方法与
with_polymorphic()
函数。
通配符加载策略¶
joinedload()、
subqueryload()、
lazyload()
、
selectInload()
和 raiseload()
可用于为特定查询设置 relationship()
加载的默认样式,从而影响语句中未另行指定的所有 relationship()
映射属性。通过将字符串 '*'
作为参数传递给以下选项中的任何一个,可以使用此功能:
from sqlalchemy import select
from sqlalchemy.orm import lazyload
stmt = select(MyClass).options(lazyload("*"))
在上面,lazyload('*')
选项将取代该查询使用的所有 relationship()
结构的 lazy
设置,但使用 lazy='write_only'
的除外
或 lazy='dynamic'
来获取。
如果某些关系指定
lazy='joined'
或 lazy='selectin'
,例如,使用 lazyload('*')
将单方面导致所有这些关系使用 'select'
加载,例如,当访问每个属性时发出 SELECT 语句。
该选项不会取代查询中规定的加载器选项,例如 joinedload()、
selectInload()
等。下面的查询仍将对 widget
关系使用 join loading:
from sqlalchemy import select
from sqlalchemy.orm import lazyload
from sqlalchemy.orm import joinedload
stmt = select(MyClass).options(lazyload("*"), joinedload(MyClass.widget))
虽然上述 joinedload()
的指令无论出现在 lazyload()
选项之前还是之后都会发生,但如果传递了多个选项,每个选项都包含 “*”
,则最后一个选项将生效。
每个实体的通配符加载策略¶
通配符加载程序策略的一个变体是能够基于每个实体设置策略。例如,如果查询 User
和 Address
,我们可以指示 Address
上的所有关系使用延迟加载,同时让 User
的加载器策略不受影响,方法是首先应用 Load
对象,然后将 *
指定为链接选项:
from sqlalchemy import select
from sqlalchemy.orm import Load
stmt = select(User, Address).options(Load(Address).lazyload("*"))
在上面,Address
上的所有关系都将设置为延迟加载。
将显式 joins/statements 路由到 Eagerly Loaded 的集合中¶
joinedload()
的行为是这样的,使用匿名别名作为目标自动创建联接,其结果被路由到加载对象上的集合和标量引用中。通常情况下,查询已经包含表示特定集合或标量引用的必要联接,并且 joinedload 功能添加的联接是多余的 - 但您仍然希望填充集合/引用。
为此,SQLAlchemy 提供了 contains_eager()
选择。此选项的使用方式与
joinedload()
选项,但假定
Select
对象将显式包含适当的连接,通常使用像 Select.join()
这样的方法。下面,我们指定 User
和 Address
之间的联接
此外,将此建立为 User.addresses
预先加载的基础:
from sqlalchemy.orm import contains_eager
stmt = select(User).join(User.addresses).options(contains_eager(User.addresses))
如果语句的 “eager” 部分是 “aliased”,则应使用 PropComparator.of_type()
指定路径,这允许传递特定的 aliased()
结构:
# use an alias of the Address entity
adalias = aliased(Address)
# construct a statement which expects the "addresses" results
stmt = (
select(User)
.outerjoin(User.addresses.of_type(adalias))
.options(contains_eager(User.addresses.of_type(adalias)))
)
# get results normally
r = session.scalars(stmt).unique().all()
SELECT
users.user_id AS users_user_id,
users.user_name AS users_user_name,
adalias.address_id AS adalias_address_id,
adalias.user_id AS adalias_user_id,
adalias.email_address AS adalias_email_address,
(...other columns...)
FROM users
LEFT OUTER JOIN email_addresses AS email_addresses_1
ON users.user_id = email_addresses_1.user_id
作为 contains_eager()
的参数给出的路径需要
设置为起始实体的完整路径。例如,如果我们在
Users->orders->Order->items->Item
,该选项将用作:
stmt = select(User).options(contains_eager(User.orders).contains_eager(Order.items))
使用 contains_eager() 加载自定义筛选的采集结果¶
当我们使用 contains_eager()
时,我们正在构建将用于填充集合的 SQL。由此,我们自然而然地可以选择修改集合要存储的值,方法是编写我们的 SQL 来加载集合或标量属性的元素子集。
提示
SQLAlchemy 现在有一种更简单的方法来做到这一点,它允许
WHERE 条件直接添加到加载器选项中,例如
joinedload()
以及使用 PropComparator.and_()
的 selectInload()
来获取。有关示例,请参阅 Adding Criteria to loader options 部分。
如果要使用比简单的 WHERE 子句更复杂的 SQL 条件或修饰符查询相关集合,则此处描述的技术仍然适用。
例如,我们可以加载一个 User
对象,并通过过滤联接的数据,使用 contains_eager()
路由它,也使用
Populate Existing 以确保覆盖任何已加载的集合:
stmt = (
select(User)
.join(User.addresses)
.filter(Address.email_address.like("%@aol.com"))
.options(contains_eager(User.addresses))
.execution_options(populate_existing=True)
)
上述查询将仅加载至少包含包含子字符串 'aol.com'
的 Address
对象的 User
对象
电子邮件
字段;User.addresses
集合将仅包含
这些 Address
条目,而不是实际上与集合关联的任何其他 Address
条目。
提示
在所有情况下,SQLAlchemy ORM 都不会覆盖已加载的
属性和集合,除非被告知这样做。 由于存在
身份映射,通常情况下,ORM 查询返回实际上已经存在并加载到内存中的对象。因此,当使用 contains_eager()
填充集合时
换句话说,使用
如上所示填充 Existing,以便使用新数据刷新已加载的集合。populate_existing
选项将重置已存在的所有属性,包括待处理的更改,因此请确保在使用前刷新所有数据。使用 Session
及其默认行为 autoflush 就足够了。
注意
我们使用 contains_eager()
加载的自定义集合
不是 “粘性”;也就是说,下次加载此集合时,它将
加载其通常的默认内容。 集合是主题
如果对象过期,则重新加载,每当
Session.commit()、
Session.rollback()
方法在假设默认会话设置的情况下使用,或者 Session.expire_all()
或 Session.expire()
方法。
另请参阅
将条件添加到加载器选项 - 现代 API,允许直接在任何关系加载器选项中使用 WHERE 标准
关系加载器 API¶
对象名称 |
描述 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-
函数 sqlalchemy.orm 中。contains_eager(*keys:Literal['*']QueryableAttribute[Any], **kw: Any)_AbstractLoad ¶
指示应从查询中手动声明的列中预先加载给定的属性。
此函数是Load
接口的一部分,支持方法链式和独立作。
该选项与加载所需行的显式连接结合使用,即:sess.query(Order).join(Order.user).options(contains_eager(Order.user))
上述查询将从Order
实体联接到其相关的User
实体,并且返回的Order
对象将具有Order.user
属性。
它也可以用于自定义 argerly loaded 中的 entries 收集;查询通常需要使用 Populate Existing execution 选项,假设父对象的主集合可能已经加载:sess.query(User).join(User.addresses).filter( Address.email_address.like("%@aol.com") ).options(contains_eager(User.addresses)).populate_existing()
有关完整的使用详情,请参阅 Routing Explicit Joins/Statements into Eagerly Loaded Collections 部分。
-
函数 sqlalchemy.orm 中。defaultload(*keys:Literal['*']QueryableAttribute[Any])_AbstractLoad ¶
指示属性应使用其预定义的加载程序样式加载。
此加载选项的行为是不更改属性的当前加载样式,这意味着将使用之前配置的加载样式,或者如果未选择以前的样式,则将使用默认加载。
此方法用于将其他加载器选项进一步链接到属性链中,而不会改变沿链的链接的加载器样式。例如,要为元素的元素设置 join 预先加载:session.query(MyClass).options( defaultload(MyClass.someattribute).joinedload( MyOtherClass.someotherattribute ) )
defaultload()
对于在相关类上设置列级选项(即defer()
和undefer()
的选项)也很有用:session.scalars( select(MyClass).options( defaultload(MyClass.someattribute) .defer("some_column") .undefer("some_other_column") ) )
-
函数 sqlalchemy.orm 中。immediateload(*keys:Literal['*']QueryableAttribute[Any], recursion_depth:intNone=None)_AbstractLoad ¶
指示应使用带有每个属性的 SELECT 语句的立即加载来加载给定属性。
加载是使用 “lazyloader” 策略实现的,并且不会触发任何其他 Eager Loader。immediateload()
选项通常被selectinload()
选项取代,该选项通过对所有加载的对象发出 SELECT 来更有效地执行相同的任务。
此函数是Load
接口的一部分,支持方法链式和独立作。
参数recursion_depth¶ –
可选 int;当与自引用关系一起设置为正整数时,表示 “selectIn” 加载将自动继续该多个级别的深度,直到找不到任何项目。
注意immediateload.recursion_depth
选项当前仅支持自引用关系。目前还没有一个选项可以自动遍历涉及多个关系的递归结构。
警告
此参数是新的实验性参数,应被视为“alpha”状态
2.0 版本中的新功能:添加了immediateload.recursion_depth
-
函数 sqlalchemy.orm 中。joinedload(*keys:Literal['*']QueryableAttribute[Any], **kw: Any)_AbstractLoad ¶
指示应使用联接的预先加载来加载给定的属性。
此函数是Load
接口的一部分,支持方法链式和独立作。
例子:# joined-load the "orders" collection on "User" select(User).options(joinedload(User.orders)) # joined-load Order.items and then Item.keywords select(Order).options(joinedload(Order.items).joinedload(Item.keywords)) # lazily load Order.items, but when Items are loaded, # joined-load the keywords collection select(Order).options(lazyload(Order.items).joinedload(Item.keywords))
参数innerjoin¶ –
如果为 True
,则指示加入的 Eager Load 应使用 inner join,而不是默认的 left outer join:select(Order).options(joinedload(Order.user, innerjoin=True))
为了将多个 Eager 连接链接在一起,其中一些可能是 OUTER 而另一些是 INNER,使用右嵌套连接来链接它们:select(A).options( joinedload(A.bs, innerjoin=False).joinedload(B.cs, innerjoin=True) )
上面的查询,通过 “outer” join 链接 A.bs,通过 “inner” join 链接B.cs,将连接呈现为 “a LEFT OUTER JOIN (b JOIN c)”。当使用旧版本的 SQLite (< 3.7.16) 时,这种形式的 JOIN 被转换为使用完整的子查询,因为此语法不直接受支持。innerjoin
标志也可以用术语“unnested”
来表示。这表示应该使用 INNER JOIN,除非该联接链接到左侧的 LEFT OUTER JOIN,在这种情况下,它将呈现为 LEFT OUTER JOIN。例如,假设A.bs
是一个 outerjoin:select(A).options(joinedload(A.bs).joinedload(B.cs, innerjoin="unnested"))
上述联接将呈现为“a LEFT OUTER JOIN b LEFT OUTER JOIN c”,而不是“a LEFT OUTER JOIN (b JOIN c)”。
注意
“unnested” 标志不会影响渲染的 JOIN 从多对多关联表中,例如配置为relationship.secondary
添加到目标表;为了结果的正确性,这些连接始终是 INNER,因此如果链接到 OUTER 连接,则它们是右嵌套的。
注意joinedload()
生成的连接是匿名的 别名。无法修改 join 继续进行的条件,启用 ORM 的Select
或 legacy 也无法修改Query
以任何方式引用这些联接,包括排序。有关更多详细信息,请参阅 The Zen of Joined Eager Loading。
要生成显式可用的特定 SQL JOIN,请使用Select.join()
和Query.join() 的 Query.join()
中。要合并 显式 JOIN,使用contains_eager()
;参见 Routing Explicit Joins/Statements into Eagerly Loaded Collections。
-
函数 sqlalchemy.orm 中。lazyload(*keys:Literal['*']QueryableAttribute[Any])_AbstractLoad ¶
指示应使用 “lazy” 加载来加载给定的属性。
此函数是Load
接口的一部分,支持方法链式和独立作。
-
类 sqlalchemy.orm 中。加载¶
表示加载器选项,这些选项修改启用了 ORM 的Select
或遗留Query
的状态,以影响各种映射属性的加载方式。
在大多数情况下,当使用像joinedload()
这样的查询选项时,Load
对象在幕后隐式使用。defer()
或类似的它通常不会直接实例化,除非在某些非常特殊的情况下。
另请参阅
Per-Entity Wildcard Loading Strategies (按实体通配符加载策略) - 说明了直接使用Load
可能有用的示例
成员
contains_eager()、defaultload()、defer()、get_children()、immediateload()、inherit_cache、joinedload()、lazyload()、load_only()、noload()、options()、process_compile_state()process_compile_state_replaced_entities()、propagate_to_loaders、raiseload()、selectin_polymorphic()、selectinload()、subqueryload()、undefer()、undefer_group()、with_expression()
类签名
类sqlalchemy.orm.Load
() ()sqlalchemy.orm.strategy_options._AbstractLoad
-
方法sqlalchemy.orm.Load 中。
contains_eager(attr: _AttrType, alias:_FromClauseArgumentNone=无, _is_chain: bool = False, _propagate_to_loaders: bool = false)Self ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.contains_eager
sqlalchemy.orm.strategy_options._AbstractLoad
使用contains_eager()
选项。
有关使用示例,请参阅contains_eager()。
-
方法sqlalchemy.orm.Load 中。
defaultload(attr:Literal['*']QueryableAttribute[Any])自身 ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.defaultload
sqlalchemy.orm.strategy_options._AbstractLoad
使用defaultload()
选项。
有关使用示例,请参阅defaultload()
。
-
方法sqlalchemy.orm.Load 中。
defer(key:Literal['*']QueryableAttribute[Any], raiseload: bool = False)自身 ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.defer
sqlalchemy.orm.strategy_options._AbstractLoad
使用defer()
选项。
有关使用示例,请参阅defer()
。
-
方法sqlalchemy.orm.Load 中。
get_children(*, omit_attrs: 元组[str, ...] = (), **kw: Any)Iterable[HasTraverseInternals] ¶
继承自HasTraverseInternals.get_children()
HasTraverseInternals
的方法
返回紧接的子HasTraverseInternals
元素中
。
这用于访问遍历。
kw 可能包含更改返回的集合的标志,例如,返回项的子集以减少较大的遍历,或从不同的上下文返回子项(例如架构级集合而不是子句级集合)。
-
方法sqlalchemy.orm.Load 中。
immediateload(attr:Literal['*']QueryableAttribute[Any], recursion_depth:intNone=None)自身 ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.immediateload
sqlalchemy.orm.strategy_options._AbstractLoad
使用immediateload()
选项。
有关使用示例,请参阅immediateload()
。
-
属性sqlalchemy.orm.Load 的 Load。
inherit_cache:boolNone = 无¶
继承自HasCacheKey
的HasCacheKey.inherit_cache
属性
指示此HasCacheKey
实例是否应使用其直接超类使用的缓存键生成方案。
该属性默认为None
,这表示构造尚未考虑是否适合参与缓存;这在功能上等效于将值设置为False
,但还会发出警告。
如果与对象对应的 SQL 不基于此类的本地属性而不是其超类而更改,则可以在特定类上将此标志设置为True
。
另请参阅
启用对自定义构造的缓存支持 - 设置HasCacheKey.inherit_cache
第三方或用户定义的 SQL 构造的属性。
-
方法sqlalchemy.orm.Load 中。
joinedload(attr:Literal['*']QueryableAttribute[Any], innerjoin:boolNone=None)自身 ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.joinedload
sqlalchemy.orm.strategy_options._AbstractLoad
使用joinedload()
选项。
有关使用示例,请参阅joinedload()
。
-
方法sqlalchemy.orm.Load 中。
lazyload(attr:Literal['*']QueryableAttribute[Any])自身 ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.lazyload
sqlalchemy.orm.strategy_options._AbstractLoad
使用lazyload()
选项。
有关使用示例,请参阅lazyload()。
-
方法sqlalchemy.orm.Load 中。
load_only(*attrs:Literal['*']QueryableAttribute[Any], raiseload: bool = False)自身 ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.load_only
sqlalchemy.orm.strategy_options._AbstractLoad
使用load_only()
选项。
有关使用示例,请参阅load_only()
。
-
方法sqlalchemy.orm.Load 中。
noload(attr:Literal['*']QueryableAttribute[Any])自身 ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.noload
sqlalchemy.orm.strategy_options._AbstractLoad
使用noload()
选项。
有关使用示例,请参阅noload()
。
-
方法sqlalchemy.orm.Load 中。
options(*opts: _AbstractLoad)Self ¶
将一系列选项作为子选项应用于此负荷
对象。
例如:query = session.query(Author) query = query.options( joinedload(Author.book).options( load_only(Book.summary, Book.excerpt), joinedload(Book.citations).options(joinedload(Citation.author)), ) )
在 1.3.6 版本加入.
-
方法sqlalchemy.orm.Load 中。
process_compile_state(compile_state: ORMCompileState)无 ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.process_compile_state
sqlalchemy.orm.strategy_options._AbstractLoad
将修改应用于给定的ORMCompileState
。
此方法是特定CompileStateOption
的,并且仅在编译 ORM 查询时在内部调用。
-
方法sqlalchemy.orm.Load 中。
process_compile_state_replaced_entities(compile_state: ORMCompileState, mapper_entities: Sequence[_MapperEntity])无 ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.process_compile_state_replaced_entities
sqlalchemy.orm.strategy_options._AbstractLoad
对给定的ORMCompileState
应用修改,给定的实体被 with_only_columns() 或 with_entities() 替换。
此方法是特定CompileStateOption
的,并且仅在编译 ORM 查询时在内部调用。
在 1.4.19 版本加入.
-
属性sqlalchemy.orm.Load 的 Load。
propagate_to_loaders: bool (布尔值)¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.propagate_to_loaders
sqlalchemy.orm.strategy_options._AbstractLoad
如果为 True,则指示此选项应应用于关系延迟加载器以及属性加载/刷新作的“辅助”SELECT 语句。
-
方法sqlalchemy.orm.Load 中。
raiseload(attr:Literal['*']QueryableAttribute[Any], sql_only: bool = False)自身 ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.raiseload
sqlalchemy.orm.strategy_options._AbstractLoad
使用raiseload()
选项。
有关使用示例,请参阅raiseload()
。
-
方法sqlalchemy.orm.Load 中。
selectin_polymorphic(classes: Iterable[Type[Any]])自身 ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.selectin_polymorphic
sqlalchemy.orm.strategy_options._AbstractLoad
使用selectin_polymorphic()
选项。
有关使用示例,请参阅selectin_polymorphic()。
-
方法sqlalchemy.orm.Load 中。
selectinload(attr:Literal['*']QueryableAttribute[Any], recursion_depth:intNone=None)自我 ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.selectinload
sqlalchemy.orm.strategy_options._AbstractLoad
使用selectInload()
选项。
有关使用示例,请参阅selectinload()
。
-
方法sqlalchemy.orm.Load 中。
subqueryload(attr:Literal['*']QueryableAttribute[Any])自身 ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.subqueryload
sqlalchemy.orm.strategy_options._AbstractLoad
使用subqueryload()
选项。
有关用法示例,请参阅subqueryload()
。
-
方法sqlalchemy.orm.Load 中。
undefer(key:Literal['*']QueryableAttribute[Any])Self ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.undefer
sqlalchemy.orm.strategy_options._AbstractLoad
使用undefer()
选项。
有关用法示例,请参阅undefer()
。
-
方法sqlalchemy.orm.Load 中。
undefer_group(name: str)Self ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.undefer_group
sqlalchemy.orm.strategy_options._AbstractLoad
使用undefer_group()
选项。
有关使用示例,请参阅undefer_group()。
-
方法sqlalchemy.orm.Load 中。
with_expression(key: _AttrType, expression: _ColumnExpressionArgument[Any])Self ¶
继承自sqlalchemy.orm.strategy_options._AbstractLoad.with_expression
sqlalchemy.orm.strategy_options._AbstractLoad
使用with_expression()
选项。
有关使用示例,请参阅with_expression()
。
-
-
函数 sqlalchemy.orm 中。noload(*keys:Literal['*']QueryableAttribute[Any])_AbstractLoad ¶
指示给定的关系属性应保持未加载状态。
relationship 属性在访问时将返回None
,而不会产生任何加载效果。
此函数是Load
接口的一部分,支持方法链式和独立作。noload()
仅适用于relationship()
属性。
旧版功能noload()
选项是 legacy 选项。因为它迫使集合为空,这总是会导致非直观且难以预测的结果。在现代 SQLAlchemy 中没有此选项的合法用途。
另请参阅
-
函数 sqlalchemy.orm 中。raiseload(*keys:Literal['*']QueryableAttribute[Any], **kw: Any)_AbstractLoad ¶
指示给定的属性在访问时应引发错误。
使用raiseload()
配置的关系属性将在访问时引发InvalidRequestError
。这非常有用的典型方式是,当应用程序尝试确保在特定上下文中访问的所有关系属性都已通过预先加载加载时。无需阅读 SQL 日志来确保不会发生延迟加载,此策略将导致它们立即引发。raiseload()
仅适用于relationship()
属性。为了将 raise-on-SQL 行为应用于基于列的属性,请在defer()
loader 选项。
参数
sql_only¶ – 如果为 True,则仅在延迟加载会发出 SQL 时引发,但如果它只是检查身份映射,或者由于缺少键而确定相关值应该只是 None,则不引发。当 False 时,该策略将针对各种关系加载加注。
此函数是Load
接口的一部分,支持方法链式和独立作。
-
函数 sqlalchemy.orm 中。selectinload(*keys:Literal['*']QueryableAttribute[Any], recursion_depth:intNone=None)_AbstractLoad ¶
指示应使用 SELECT IN 预先加载来加载给定的属性。
此函数是Load
接口的一部分,支持方法链式和独立作。
例子:# selectin-load the "orders" collection on "User" select(User).options(selectinload(User.orders)) # selectin-load Order.items and then Item.keywords select(Order).options( selectinload(Order.items).selectinload(Item.keywords) ) # lazily load Order.items, but when Items are loaded, # selectin-load the keywords collection select(Order).options(lazyload(Order.items).selectinload(Item.keywords))
参数recursion_depth¶ –
可选 int;当与自引用关系一起设置为正整数时,表示 “selectIn” 加载将自动继续该多个级别的深度,直到找不到任何项目。
注意selectinload.recursion_depth
选项当前仅支持自引用关系。目前还没有一个选项可以自动遍历涉及多个关系的递归结构。
此外,selectinload.recursion_depth
parameter 是新的和实验性的,应被视为 “alpha” 状态。
2.0 版本中的新功能:添加了selectinload.recursion_depth
-
函数 sqlalchemy.orm 中。subqueryload(*keys:Literal['*']QueryableAttribute[Any])_AbstractLoad ¶
指示应使用 subquery 预先加载来加载给定的属性。
此函数是Load
接口的一部分,支持方法链式和独立作。
例子:# subquery-load the "orders" collection on "User" select(User).options(subqueryload(User.orders)) # subquery-load Order.items and then Item.keywords select(Order).options( subqueryload(Order.items).subqueryload(Item.keywords) ) # lazily load Order.items, but when Items are loaded, # subquery-load the keywords collection select(Order).options(lazyload(Order.items).subqueryload(Item.keywords))