关系加载技术
sqlAlchemy的一个重要部分是提供了一个广泛的控制范围,用于控制查询时相关对象的加载方式。通过“相关对象”,我们引用在映射器上配置的集合或标量关联,使用 relationship()
. 可以在映射器构造时使用 relationship.lazy
参数 relationship()
函数,以及使用选项 Query
对象。
关系的负载分为三类; lazy 加载, 渴望的 加载,以及 no 加载。延迟加载指的是从查询返回的对象,而不首先加载相关的对象。当第一次在特定对象上访问给定的集合或引用时,会发出一条附加的select语句,以便加载请求的集合。
预加载是指从查询返回的对象,其中相关集合或标量引用已预先加载。这个 Query
实现这一点的方法是,通过增加select语句,它通常会发出一个join来同时加载相关行,或者在主select语句之后发出额外的select语句来一次加载集合或标量引用。
“否”加载是指禁用加载给定关系,或者属性为空且从未加载,或者在访问时引发错误,以防止不必要的延迟加载。
关系加载的主要形式是:
延迟加载 -可通过
lazy='select'
或lazyload()
选项,这是一种加载形式,在属性访问时发出一条select语句,以便一次惰性地加载单个对象上的相关引用。延迟加载的详细信息 延迟加载 .联合加载 -可通过
lazy='joined'
或joinedload()
选项,此加载形式将联接应用于给定的select语句,以便在同一结果集中加载相关行。联合预加载在 加入的热切装载 .子查询加载 -可通过
lazy='subquery'
或subqueryload()
选项,此加载形式发出第二个select语句,该语句重新声明嵌入在子查询中的原始查询,然后将该子查询联接到要加载的相关表,以一次加载相关集合/标量引用的所有成员。子查询预加载的详细信息位于 子查询预加载 .加载时选择 -可通过
lazy='selectin'
或selectinload()
选项,此加载形式发出第二个(或更多)select语句,将父对象的主键标识符组装到in子句中,以便相关集合/标量引用的所有成员都按主键一次加载。加载中的选择详细信息位于 加载时选择 .加荷 -可通过
lazy='raise'
,lazy='raise_on_sql'
,或者raiseload()
选项,此形式的加载在通常发生延迟加载的同时触发,除非它引发ORM异常以防止应用程序进行不需要的延迟加载。提升负载的介绍在 使用raiseload防止不需要的懒惰负载 .未装货 -通过以下方式提供
lazy='noload'
,或noload()
选项;此加载样式将属性转换为空属性 (None
或[]
),它永远不会加载或有任何加载效果。当加载对象时,这个很少使用的策略的行为有点像是一个紧急加载程序,因为它放置了一个空属性或集合,但是对于过期的对象,依赖于访问时返回的属性的默认值;除了属性名称是否出现在InstanceState.unloaded
收藏。noload
对于实现“只写”属性可能很有用,但是这种用法目前没有经过测试或正式支持。
在映射时配置加载程序策略
特定关系的加载器策略可以在映射时配置为在加载映射类型的对象的所有情况下发生,而不需要修改它的任何查询级别选项。这是使用 relationship.lazy
参数到 relationship()
;此参数的常用值包括 select
, joined
, subquery
和 selectin
.
例如,要将关系配置为在查询父对象时使用Joined Earge Loading,请执行以下操作:
class Parent(Base): __tablename__ = 'parent' id = Column(Integer, primary_key=True) children = relationship("Child", lazy='joined')
上面,每当 Parent
加载对象,每个 Parent
也会有它的 children
集合已填充,使用通过向查询中添加联接获取的行 Parent
物体。见 加入的热切装载 对于这种加载方式的背景。
的默认值 relationship.lazy
论证是 "select"
,表示延迟加载。见 延迟加载 了解更多背景。
与加载程序选项的关系加载
另一种配置加载策略的更常见方法是使用 Query.options()
方法。使用加载程序选项可以非常详细地控制关系加载;最常见的是 joinedload()
, subqueryload()
, selectinload()
和 lazyload()
. 该选项接受属性相对于父级的字符串名称,或者为了更高的特异性,可以直接容纳类绑定属性:
# set children to load lazily session.query(Parent).options(lazyload(Parent.children)).all() # set children to load eagerly with a join session.query(Parent).options(joinedload(Parent.children)).all()
加载程序选项也可以使用 方法链 若要指定在更深的层次上加载的方式,请执行以下操作:
session.query(Parent).options( joinedload(Parent.children). subqueryload(Child.subelements)).all()
可以对“惰性”加载的集合应用链接加载程序选项。这意味着,当访问时延迟加载集合或关联时,指定的选项将生效::
session.query(Parent).options( lazyload(Parent.children). subqueryload(Child.subelements)).all()
在上面,查询将返回 Parent
没有的对象 children
集合已加载。当 children
一个特定的集合 Parent
对象首先被访问,它将延迟加载相关的对象,但还将预先加载应用于 subelements
每个成员的集合 children
.
上面的例子,使用 Query
,现在称为 1.x style 查询。选项系统也可用于 2.0 style 使用 Select.options()
方法:
stmt = select(Parent).options( lazyload(Parent.children). subqueryload(Child.subelements)) result = session.execute(stmt)
在引擎盖下, Query
最终使用的是上述方法 select
基于机制。
向加载程序选项添加条件
用于指示装入器选项的关系属性包括向所创建的联接的ON子句或所涉及的WHERE条件添加附加筛选条件的能力,具体取决于装入器策略。可以使用 PropComparator.and_()
方法,该方法将传递一个选项,以便加载的结果仅限于给定的筛选条件:
session.query(A).options(lazyload(A.bs.and_(B.id > 5)))
使用限制条件时,如果特定集合已加载,则不会刷新该集合;若要确保执行新条件,请将 Query.populate_existing()
选项::
session.query(A).options(lazyload(A.bs.and_(B.id > 5))).populate_existing()
为了将筛选条件添加到整个查询中实体的所有出现处,而不考虑加载程序策略或加载过程中的位置,请参阅 with_loader_criteria()
功能。
1.4 新版功能.
使用指定子选项加载选项()
使用方法链接,明确地说明了路径中每个链接的加载程序样式。要沿路径导航而不更改特定属性的现有加载程序样式,请 defaultload()
方法/函数可用于:
session.query(A).options( defaultload(A.atob). joinedload(B.btoc)).all()
可以使用类似的方法一次指定多个子选项,使用 Load.options()
方法:
session.query(A).options( defaultload(A.atob).options( joinedload(B.btoc), joinedload(B.btod) )).all()
1.3.6 新版功能: 补充 Load.options()
参见
跨多个实体延迟加载 -说明了组合关系和面向列的加载程序选项的示例。
注解
应用于对象的惰性加载集合的加载程序选项为 “粘” 对于特定的对象实例,这意味着只要特定对象存在于内存中,它们就会一直存在于由该特定对象加载的集合上。例如,给出前面的示例:
session.query(Parent).options( lazyload(Parent.children). subqueryload(Child.subelements)).all()
如果 children
一个特定的集合 Parent
上述查询加载的对象已过期(例如 Session
对象的事务已提交或回滚,或者 Session.expire_all()
使用),当 Parent.children
下一次访问集合是为了重新加载它, Child.subelements
将再次使用子查询预加载加载来加载集合。即使上面 Parent
从指定不同选项集的后续查询中访问对象。若要更改现有对象上的选项而不将其删除并重新加载,必须将这些选项与 Query.populate_existing()
方法:
# change the options on Parent objects that were already loaded session.query(Parent).populate_existing().options( lazyload(Parent.children). lazyload(Child.subelements)).all()
如果上面加载的对象已从 Session
,例如由于垃圾收集或 Session.expunge_all()
如果再次加载,则“粘性”选项也将消失,新创建的对象将使用新选项。
未来的sqlAlchemy版本可能会添加更多的选项来操作已经加载的对象上的加载程序选项。
延迟加载
默认情况下,所有对象间关系都是 延迟加载 . 与 relationship()
包含第一次访问属性时激发的触发器。此触发器通常在访问点发出SQL调用,以便加载相关对象:
>>> jack.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'jack@google.com')>, <Address(u'j25@yahoo.com')>]
不发出SQL的一种情况是对于简单的多对一关系,当相关对象可以通过其主键单独标识,并且该对象已存在于当前 Session
. 因此,尽管延迟加载对于相关的集合来说可能很昂贵,但是如果一个人正在加载许多对象,其中简单的多对一对象对应一组相对较小的可能目标对象,则延迟加载可能能够在本地引用这些对象,而不会发出与父对象相同的SELECT语句。
“属性访问时加载”的默认行为称为“lazy”或“select”加载-名称为“select”,因为“select”语句通常在首次访问属性时发出。
可以为通常以其他方式配置的给定属性启用延迟加载,方法是使用 lazyload()
加载器选项:
from sqlalchemy.orm import lazyload # force lazy loading for an attribute that is set to # load some other way normally session.query(User).options(lazyload(User.addresses))
使用raiseload防止不需要的懒惰负载
这个 lazyload()
策略产生的效果是对象关系映射中提到的最常见的问题之一; N plus one problem ,表示对于任何加载的n个对象,访问其延迟加载的属性意味着将发出n+1 select语句。在sqlAlchemy中,通常缓解n+1问题的方法是利用它的非常强大的热切加载系统。但是,预加载要求使用指定要加载的属性 Query
在前面。可以使用 raiseload()
策略;此加载程序策略用引发的信息性错误替换延迟加载的行为:
from sqlalchemy.orm import raiseload session.query(User).options(raiseload(User.addresses))
以上,A User
从上述查询加载的对象将不具有 .addresses
集合已加载;如果稍后某些代码尝试访问此属性,则会引发ORM异常。
raiseload()
可以与所谓的“通配符”说明符一起使用,以指示所有关系都应使用此策略。例如,仅将一个属性设置为“热切加载”,其余属性设置为“提升”::
session.query(Order).options( joinedload(Order.items), raiseload('*'))
以上通配符将应用于 all 不仅仅是关系 Order
此外 items
但是所有的那些 Item
对象也一样。设立 raiseload()
只为 Order
对象,指定完整路径 Load
::
from sqlalchemy.orm import Load session.query(Order).options( joinedload(Order.items), Load(Order).raiseload('*'))
相反,只为 Item
物体::
session.query(Order).options( joinedload(Order.items).raiseload('*'))
这个 raiseload()
选项仅适用于关系属性。对于面向列的属性 defer()
选项支持 defer.raiseload
以相同方式工作的选项。
在 1.4.0 版更改: “提货”战略 不会发生 在工作单元刷新过程中,从SQLAlChemy 1.4.0开始。这意味着如果工作单元需要加载特定属性以完成其工作,它将执行加载。要防止在UOW流程中发生特定的关系加载并不总是很容易,特别是对于不太常见的关系类型。LAZY=“RAISE”情况更适合于应用程序空间内的显式属性访问。
参见
加入的热切装载
联合预加载是ORM中最基本的预加载方式。它的工作方式是将一个联接(默认情况下是左外部联接)连接到由 Query
并从与父级相同的结果集填充目标标量/集合。
在映射级别,如下所示:
class Address(Base): # ... user = relationship(User, lazy="joined")
联合的预加载通常作为查询的选项应用,而不是作为映射上的默认加载选项应用,特别是用于集合而不是多对一引用时。这是通过使用 joinedload()
加载器选项:
>>> jack = session.query(User).\ ... options(joinedload(User.addresses)).\ ... filter_by(name='jack').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 = ? ['jack']
默认情况下发出的联接是左外部联接,以允许不引用相关行的前导对象。对于保证具有元素的属性,例如引用外键不为空的相关对象的多对一引用,可以通过使用内部联接提高查询效率;这在映射级别通过 relationship.innerjoin
旗帜:
class Address(Base): # ... user_id = Column(ForeignKey('users.id'), nullable=False) user = relationship(User, lazy="joined", innerjoin=True)
在查询选项级别,通过 joinedload.innerjoin
旗帜:
session.query(Address).options( joinedload(Address.user, innerjoin=True))
当应用于包含外部联接的链时,联接将正确嵌套自身:
>>> session.query(User).options( ... joinedload(User.addresses). ... joinedload(Address.widgets, innerjoin=True)).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
在旧版本的sqlite上,可以将上面嵌套的右联接重新呈现为嵌套的子查询。旧版本的SQLAlchemy在任何情况下都会将正确的嵌套联接转换为子查询。
联合预加载和结果集批处理
当应用于集合时,联接的热切加载的中心概念是 Query
对象必须针对正在查询的前导实体消除重复行。如上文所述,如果 User
我们加载的对象涉及三个 Address
对象,SQL语句的结果将有三行;但是 Query
只返回一个 User
对象。当接收到 User
对象刚加载到前一行中,引用新的 Address
对象被定向到 User.addresses
特定对象的集合。
这个过程是非常透明的,但是确实意味着连接的抢先加载与由 Query.yield_per()
方法,当用于集合加载时。但是,用于标量引用的联接的热切加载与 Query.yield_per()
. 这个 Query.yield_per()
方法将导致在运行基于集合的联接的热切加载器时引发异常。
要使用任意大的结果数据集进行“批处理”查询,同时保持与基于集合的联接的预加载的兼容性,请发出多个SELECT语句,每个语句都使用WHERE子句引用行的子集,例如窗口化。或者,考虑使用“select-in”预先加载,即 潜在地 兼容 Query.yield_per()
,前提是正在使用的数据库驱动程序支持多个同时使用的游标(SQLite、PostgreSQL驱动程序,而不是MySQL驱动程序或SQL Server ODBC驱动程序)。
加入渴望装载的禅宗
由于加入的热切装载似乎与使用 Query.join()
它经常会导致关于何时以及如何使用它的混淆。关键是要了解 Query.join()
用于更改查询结果, joinedload()
经过很长时间 not 更改查询结果,并隐藏呈现的联接的效果,以仅允许相关对象出现。
装载机策略背后的原理是,任何一组装载方案都可以应用于特定的查询,并且 结果不会改变 -只有完全加载相关对象和集合所需的SQL语句数才会发生更改。一个特定的查询可能首先使用所有的惰性加载。在上下文中使用它之后,可能会发现总是可以访问特定的属性或集合,并且更改这些属性或集合的加载程序策略会更有效。策略可以在不修改查询的情况下进行更改,结果将保持一致,但将发出较少的SQL语句。理论上(实际上也差不多),你对 Query
将使它根据加载程序策略的更改加载不同的一组主要对象或相关对象。
怎么用? joinedload()
尤其是在不影响以任何方式返回的实体行的情况下,它会为添加到查询中的联接创建一个匿名别名,这样它们就不能被查询的其他部分引用。例如,下面的查询使用 joinedload()
创建左外部联接的步骤 users
到 addresses
然而 ORDER BY
添加反对 Address.email_address
无效-的 Address
查询中未命名实体:
>>> jack = session.query(User).\ ... options(joinedload(User.addresses)).\ ... filter(User.name=='jack').\ ... order_by(Address.email_address).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 ! ['jack']
上面, ORDER BY addresses.email_address
无效原因 addresses
不在“发件人”列表中。加载的正确方法 User
记录和按电子邮件地址排序 Query.join()
:
>>> jack = session.query(User).\ ... join(User.addresses).\ ... filter(User.name=='jack').\ ... order_by(Address.email_address).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 ['jack']
当然,上面的语句与前面的语句不同,因为 addresses
完全不包括在结果中。我们可以添加 joinedload()
返回,以便有两个联接-一个是我们正在订购的联接,另一个是匿名用于加载 User.addresses
收藏:
>>> jack = session.query(User).\ ... join(User.addresses).\ ... options(joinedload(User.addresses)).\ ... filter(User.name=='jack').\ ... order_by(Address.email_address).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 ['jack']
我们在上面看到的是 Query.join()
是提供要在后续查询条件中使用的联接子句,而我们的用法是 joinedload()
只考虑加载 User.addresses
收集,每个 User
在结果中。在这种情况下,这两个连接最有可能看起来是冗余的——它们是冗余的。如果我们只想使用一个join来装载和订购集合,那么我们使用 contains_eager()
选项,如所述 将显式联接/语句路由到热切加载的集合中 下面。但看看为什么 joinedload()
做它所做的,考虑我们是否 过滤 关于某一特定 Address
:
>>> jack = session.query(User).\ ... join(User.addresses).\ ... options(joinedload(User.addresses)).\ ... filter(User.name=='jack').\ ... filter(Address.email_address=='someaddress@foo.com').\ ... 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 = ? ['jack', 'someaddress@foo.com']
上面,我们可以看到两个连接具有非常不同的角色。一行与一行完全匹配,即 User
和 Address
在哪里? Address.email_address=='someaddress@foo.com'
. 另一个左外部联接将匹配 all Address
相关的行 User
,并且仅用于填充 User.addresses
收藏,为那些 User
返回的对象。
通过改变 joinedload()
对于另一种加载方式,我们可以更改完全独立于用于检索实际 User
我们想要的行。下面我们改变 joinedload()
进入之内 subqueryload()
:
>>> jack = session.query(User).\ ... join(User.addresses).\ ... options(subqueryload(User.addresses)).\ ... filter(User.name=='jack').\ ... filter(Address.email_address=='someaddress@foo.com').\ ... 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 = ? ['jack', 'someaddress@foo.com'] # ... subqueryload() emits a SELECT in order # to load all address records ...
当使用joined earch loading时,如果查询包含影响从外部返回到联接的行的修饰符(例如,当使用distinct、limit、offset或等效值时),则完成的语句首先包装在子查询中,并且专门用于joined earch loading的联接将应用于子查询。sqlAlchemy的joined earch loading将增加一英里,然后再增加十英里,以确保它不会影响查询的最终结果,只会影响集合和相关对象的加载方式,不管查询的格式是什么。
参见
将显式联接/语句路由到热切加载的集合中 - using contains_eager()
子查询预加载
子查询加载预加载的配置方式与联接的预加载相同;用于 relationship.lazy
参数,我们将指定 "subquery"
而不是 "joined"
对于选项,我们使用 subqueryload()
选项而不是 joinedload()
选择权。
子查询预加载的操作是为要加载的每个关系发出第二条SELECT语句,一次跨所有结果对象。此select语句引用包装在子查询中的原始select语句,以便我们检索所返回的主对象的相同主键列表,然后将其链接到所有集合成员的总和,以便一次加载它们:
>>> jack = session.query(User).\ ... options(subqueryload(User.addresses)).\ ... filter_by(name='jack').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 = ? ('jack',) 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 ('jack',)
在加载集合方面,子查询加载策略与联合的预加载相比具有许多优势。首先,它允许原始查询在不进行任何更改的情况下继续进行,特别是不引入可能降低效率的左外部联接。其次,它允许在不生成具有多个联接的单个查询的情况下急切地加载多个集合,这样效率甚至更低;每个关系都加载在完全独立的查询中。最后,由于附加查询只需要加载集合项而不需要Lead对象,因此它可以在所有情况下使用内部联接以提高查询效率。
子查询负载的缺点包括原始查询的复杂性被转移到关系查询中,当与子查询结合使用时,在某些情况下(尤其是mysql)的某些后端会产生非常慢的查询。此外,子查询加载策略只能一次加载所有集合的全部内容,因此与由提供的“批处理”加载不兼容。 Query.yield_per()
对于集合和标量关系。
由提供的较新加载样式 selectinload()
解决了 subqueryload()
.
参见
订购的重要性
利用 subqueryload()
与限制性修饰语如 Query.first()
, Query.limit()
或 Query.offset()
应该 总是 包括 Query.order_by()
针对唯一列(如主键),以便 subqueryload()
包括与父查询使用的顺序相同的顺序。如果没有它,内部查询可能返回错误的行:
# incorrect, no ORDER BY session.query(User).options( subqueryload(User.addresses)).first() # incorrect if User.name is not unique session.query(User).options( subqueryload(User.addresses) ).order_by(User.name).first() # correct session.query(User).options( subqueryload(User.addresses) ).order_by(User.name, User.id).first()
参见
为什么是 ORDER BY 要求与 LIMIT (尤其是与 subqueryload() )? -详细示例
加载时选择
Select-IN-loading在操作上与subquery-eager-loading类似,但是发出的Select语句的结构要比subquery eager-loading简单得多。在大多数情况下,selectin加载是快速加载对象集合的最简单、最有效的方法。selectin预先加载不可行的唯一情况是模型使用复合主键,而后端数据库不支持in的元组(当前包括sqlserver)。
1.2 新版功能.
“select in”(在中选择)通过 "selectin"
参数 relationship.lazy
或通过使用 selectinload()
装载机选项。这种加载方式在in子句中发出一个SELECT,它引用父对象的主键值,或者在与子对象的主键值存在多对一关系的情况下,发出一个SELECT,以便加载相关的关联:
>>> jack = session.query(User).\ ... options(selectinload(User.addresses)).\ ... filter(or_(User.name == 'jack', User.name == 'ed')).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 = ? ('jack', '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)
上面第二个选择是指 addresses.user_id IN (5, 7)
,其中“5”和“7”是前两个的主键值 User
对象已加载;在一批对象完全加载后,它们的主键值被注入到 IN
用于第二次选择的子句。因为两者之间的关系 User
和 Address
有一个简单的 1 主联接条件,并提供 User
可以派生自 Address.user_id
语句完全没有联接或子查询。
在 1.3 版更改: 对于简单的一对多集合,selectin加载可以省略join。
为了简单 1 多对一加载时,也不需要联接,因为使用父对象的外键值:
>>> session.query(Address).\ ... options(selectinload(Address.user)).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)
在 1.3.6 版更改: selectin加载也可以省略简单多对一关系的联接。
- 1(1,2)
“简单”是指
relationship.primaryjoin
condition表示“one”端的主键与“many”端的直接外键之间的相等比较,无需任何附加条件。
Select-IN-loading还支持多对多关系,目前它将连接所有三个表,以匹配从一端到另一端的行。
关于这种装载需要了解的事项包括:
与“子查询”不同,“selectin”加载程序策略发出的select语句不需要子查询,也不继承原始查询的任何性能限制;查找是简单的主键查找,应具有较高的性能。
子查询负载的特殊排序要求 订购的重要性 也不适用于selectin加载;selectin总是直接链接到父主键,不能真正返回错误的结果。
与joined或subquery eager loading不同,“selectin”加载总是根据刚加载的直接父对象而不是链顶部的原始对象类型发出SELECT。因此,如果急切地加载许多级别,那么对于简单的一对多或多对一关系,“selectin”加载仍然不需要任何连接。相比之下,joined和subquery eager loading总是指向原始父级的多个联接。
当主键在SQL语句中呈现为一个大的IN表达式时,该策略一次最多发出500个父主键值的SELECT。像Oracle这样的一些数据库对IN表达式的大小有一个硬限制,总体上SQL字符串的大小不应该任意大。
由于“selectin”加载依赖于in,对于具有复合主键的映射,它必须使用in的“tuple”形式,这看起来像
WHERE (table.column_a, table.column_b) IN ((?, ?), (?, ?), (?, ?))
. SQL Server当前不支持此语法,并且SQLite至少需要3.15版。SQLAlchemy中没有特殊的逻辑来提前检查哪些平台支持此语法;如果在不支持的平台上运行,数据库将立即返回错误。SQLAlchemy的一个优点是,如果一个特定的数据库开始支持这种语法,它将工作而不会对SQLAlchemy进行任何更改(就像SQLite一样)。
一般来说,“select in”加载在大多数情况下可能优于“subquery”的预加载,除了使用复合主键的语法要求,还可能为较大的结果集发出许多select语句。和往常一样,开发人员应该花时间查看他们的应用程序在开发中生成的语句和结果,以检查事物是否有效工作。
使用哪种装载?
通常,使用哪种类型的加载来优化SQL执行次数、发出的SQL的复杂性和获取的数据量之间的权衡。我们举两个例子,A relationship()
它引用了一个集合,并且 relationship()
它引用一个标量多对一的引用。
一对多收藏
当使用默认的延迟加载时,如果加载100个对象,然后在每个对象上访问一个集合,则总共会发出101个SQL语句,尽管每个语句通常是一个简单的select,没有任何联接。
使用联合加载时,100个对象及其集合的加载将只发出一条SQL语句。但是,获取的行总数将等于所有集合的大小加上每个父对象有一个空集合的额外行的总和。每一行还将包含由父级表示的完整列集,对每个集合项重复这些列-sqlAlchemy不会重新获取这些列,而不是主键的列,但是大多数dbapis(除了某些例外)在任何情况下都会通过连接将每个父级的完整数据传输到客户端连接。因此,只有当集合的大小相对较小时,联合的热切加载才有意义。与内部联接相比,左侧外部联接也可能是性能密集型联接。
使用子查询加载时,100个对象的加载将发出两条SQL语句。第二条语句将获取等于所有集合大小之和的总行数。使用内部联接,并且至少请求父列,仅请求主键。因此,当集合较大时,子查询负载是有意义的。
当在联接或子查询加载中使用多个深度级别时,在-collections中加载集合将乘以以笛卡尔方式获取的行总数。joined和subquery都是从原始父类进行的join;如果加载一个四层深度的集合,那么将有四个join输出到父类。另一方面,selectin加载始终只有一个到直接父表的联接。
使用selectin加载,100个对象的加载还将发出两个SQL语句,其中第二个语句引用所加载对象的100个主键。但是,selectin加载将在单个select语句中呈现最多500个主键值;因此,对于大于500的潜在客户集合,将为每批选定的500个对象发出一个select语句。
在selectin加载中使用多个深度级别不会引起联接和子查询的“笛卡尔”问题;selectin加载的查询具有最佳的性能特征和最少的行数。唯一需要注意的是,根据潜在客户结果的大小,可能会发出多个选择。
与联接(使用集合时)和子查询预加载(各种关系)不同,selectin加载可能与由提供的结果集批处理兼容。
Query.yield_per()
假设有一个适当的数据库驱动程序,那么就可以允许对大型结果集进行批处理。
多对一参考
当使用默认的延迟加载时,如果集合发出多达101个SQL语句,则需要加载100个对象。但是-有一个明显的例外,即如果多对一引用是对目标主键的简单外键引用,则将首先在当前标识映射中使用
Query.get()
. 因此,在这里,如果对象集合引用了一组相对较小的目标对象,或者完整的可能目标对象集已经加载到会话中并被强引用,则使用默认的 lazy='select' 是目前为止最有效的方法。使用联合加载时,100个对象的加载将只发出一条SQL语句。联接将是一个左外部联接,在所有情况下,行总数将等于100。如果您知道每个父级肯定有一个子级(即外键引用不是空的),那么可以使用
relationship.innerjoin
设置为True
,通常在relationship()
. 对于可能存在许多尚未加载的目标引用的对象负载,使用内部联接进行联接加载非常有效。子查询加载将为所有子对象发出第二次加载,因此对于100个对象的加载,将发出两个SQL语句。然而,与联接加载相比,这里可能没有什么优势,除非子查询加载在所有情况下都可以使用内部联接,而联接加载要求外键不为空。
selectin加载还将为所有子对象发出第二个加载(如前所述,对于较大的结果,它将每500行发出一个select),因此对于100个对象的加载,将发出两个SQL语句。查询本身仍然必须联接到父表,因此对于多对一的selectin加载与联接的热切加载相比,选择in加载没有太大的优势,除了在所有情况下使用内部联接之外。
多态性预加载
支持基于每个预加载的多态选项规范。见剖面图 特异性或多态性亚型的热切加载 例如 PropComparator.of_type()
方法与 with_polymorphic()
功能。
通配符加载策略
每一个 joinedload()
, subqueryload()
, lazyload()
, selectinload()
, noload()
和 raiseload()
可用于设置默认样式 relationship()
为特定查询加载,影响所有 relationship()
-未在中另外指定映射的属性 Query
. 通过传递字符串可以使用此功能 '*'
作为这些选项的参数:
session.query(MyClass).options(lazyload('*'))
上面, lazyload('*')
选项将取代 lazy
全设置 relationship()
用于该查询的构造,但使用 'dynamic'
装载方式。如果某些关系指定 lazy='joined'
或 lazy='subquery'
,例如,使用 lazyload('*')
将单方面导致所有这些关系的使用 'select'
加载,例如在访问每个属性时发出一条SELECT语句。
该选项不会取代查询中指定的加载程序选项,例如 eagerload()
, subqueryload()
等等。下面的查询仍将使用 widget
关系:
session.query(MyClass).options( lazyload('*'), joinedload(MyClass.widget) )
中频倍数 '*'
选项被传递,最后一个选项将覆盖以前传递的选项。
每个实体的通配符加载策略
通配符加载程序策略的一个变体是能够基于每个实体设置策略。例如,如果查询 User
和 Address
,我们可以指导所有关系 Address
仅通过首先应用 Load
对象,然后指定 *
作为链接选项:
session.query(User, Address).options( Load(Address).lazyload('*'))
上面,所有关系 Address
将被设置为惰性负载。
将显式联接/语句路由到热切加载的集合中
行为 joinedload()
这样,就可以使用匿名别名作为目标自动创建联接,其结果将路由到已加载对象上的集合和标量引用中。通常情况下,查询已经包含了表示特定集合或标量引用的必要联接,并且由joinedload功能添加的联接是多余的,但是您仍然希望填充集合/引用。
对于这个sqlacalchemy提供 contains_eager()
选择权。此选项的使用方式与 joinedload()
选项,除非假设 Query
将显式指定适当的联接。下面,我们指定 User
和 Address
此外,还将其作为 User.addresses
::
class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True) addresses = relationship("Address") class Address(Base): __tablename__ = 'address' # ... q = session.query(User).join(User.addresses).\ options(contains_eager(User.addresses))
如果语句的“急切”部分是“别名”的,则应该使用 PropComparator.of_type()
,这允许特定的 aliased()
要传递的构造:
# use an alias of the Address entity adalias = aliased(Address) # construct a Query object which expects the "addresses" results query = session.query(User).\ outerjoin(User.addresses.of_type(adalias)).\ options(contains_eager(User.addresses.of_type(adalias))) # get results normally r = query.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
,则该选项将用作::
query(User).options( contains_eager(User.orders). contains_eager(Order.items))
使用contains_eager()加载自定义筛选的集合结果
当我们使用 contains_eager()
, we 正在构造将用于填充集合的SQL。由此,我们自然可以选择 修改 通过编写我们的SQL来为集合或标量属性加载元素子集,集合打算存储的值。
例如,我们可以加载一个 User
对象,并急切地仅将特定地址加载到其 .addresses
集合,方法是筛选联接的数据,并使用 contains_eager()
,也使用 Query.populate_existing()
要确保覆盖任何已加载的集合,请执行以下操作:
q = session.query(User).\ join(User.addresses).\ filter(Address.email_address.like('%@aol.com')).\ options(contains_eager(User.addresses)).\ populate_existing()
上述查询将仅加载 User
至少包含以下内容的对象 Address
对象,该对象包含子字符串。 'aol.com'
在ITS中 email
字段; User.addresses
集合将包含 only 这些 Address
条目,以及 not 任何其他 Address
实际上与集合相关联的条目。
小技巧
在所有情况下,SQLAlChemy ORM都是这样做的 不覆盖已加载的属性和集合 除非你让我这么做。因为有一个 identity map 在使用中,经常会出现这样的情况:ORM查询返回的对象实际上已经存在并加载到内存中。因此,在使用 contains_eager()
若要以另一种方式填充集合,通常最好使用 Query.populate_existing()
如上所述,以便用新数据刷新已经加载的集合。 Query.populate_existing()
将重置 all 属性,包括挂起的更改,因此请确保在使用数据之前刷新所有数据。使用 Session
它的默认行为是 autoflush 就足够了。
注解
我们使用以下命令加载的自定义集合 contains_eager()
不是“粘滞的”;也就是说,下次加载此集合时,将使用其通常的默认内容进行加载。如果对象过期,则集合可能会被重新加载,每当 Session.commit()
, Session.rollback()
方法是假定默认会话设置使用的,或者 Session.expire_all()
或 Session.expire()
本文采用的是研究方法。
创建自定义加载规则
Deep Alchemy
这是一种先进的技术!应该非常小心和测试。
ORM有各种边缘情况,其中属性的值在本地可用,但是ORM本身没有意识到这一点。在某些情况下,需要用户定义的加载属性系统。为了支持用户定义的加载系统的用例,一个关键功能 set_committed_value()
提供。这个函数基本上等同于python自己的 setattr()
函数,除了应用于目标对象时,将绕过用于确定刷新时间更改的SQLAlchemy的“属性历史记录”系统;该属性的分配方式与ORM从数据库以这种方式加载该属性的方式相同。
使用 set_committed_value()
可以与另一个称为 InstanceEvents.load()
要在加载对象时产生属性填充行为,请执行以下操作。一个这样的例子是双向的“一对一”情况,在这种情况下,加载一对一的“多对一”端也应该暗示“一对多”端的值。SQLAlChemy ORM在加载相关对象时不考虑后向引用,它将“一对一”视为另一种“一对多”,只是恰好是一行而已。
给定以下映射:
from sqlalchemy import Integer, ForeignKey, Column from sqlalchemy.orm import relationship, backref from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class A(Base): __tablename__ = 'a' id = Column(Integer, primary_key=True) b_id = Column(ForeignKey('b.id')) b = relationship( "B", backref=backref("a", uselist=False), lazy='joined') class B(Base): __tablename__ = 'b' id = Column(Integer, primary_key=True)
如果我们查询 A
排成一排,然后要求 a.b.a
,我们将得到一个额外的选择:
>>> a1.b.a SELECT a.id AS a_id, a.b_id AS a_b_id FROM a WHERE ? = a.b_id
此选择是多余的,因为 b.a
与相同的值 a1
. 我们可以创建一个加载规则来为我们填充:
from sqlalchemy import event from sqlalchemy.orm import attributes @event.listens_for(A, "load") def load_b(target, context): if 'b' in target.__dict__: attributes.set_committed_value(target.b, 'a', target)
现在当我们查询 A
我们会得到 A.b
从加入的热切负荷,和 A.b.a
从我们的事件:
a1 = s.query(A).first() SELECT a.id AS a_id, a.b_id AS a_b_id, b_1.id AS b_1_id FROM a LEFT OUTER JOIN b AS b_1 ON b_1.id = a.b_id LIMIT ? OFFSET ? (1, 0) assert a1.b.a is a1
关系加载器API
Object Name | Description |
---|---|
contains_eager(*keys, **kw) | 指示应该从查询中手动声明的列中热切地加载给定的属性。 |
defaultload(*keys) | 指示应使用默认加载程序样式加载属性。 |
eagerload(*args, **kwargs) | 同义词 |
immediateload(*keys) | 指示应使用带有per attribute select语句的即时加载来加载给定的属性。 |
joinedload(*keys, **kw) | 指示应使用joined eager loading加载给定的属性。 |
lazyload(*keys) | 指示应使用“lazy”加载来加载给定的属性。 |
表示修改 | |
noload(*keys) | 指示给定的关系属性应保持卸载状态。 |
raiseload(*keys, **kw) | 指示给定属性在访问时应引发错误。 |
selectinload(*keys) | 指示应使用select在预加载中加载给定的属性。 |
subqueryload(*keys) | 指示应使用子查询预加载来加载给定的属性。 |
- function sqlalchemy.orm.contains_eager(*keys, **kw)¶
指示应该从查询中手动声明的列中热切地加载给定的属性。
此函数是
Load
接口和支持方法链接和独立操作。该选项与加载所需行的显式联接一起使用,即:
sess.query(Order).\ join(Order.user).\ options(contains_eager(Order.user))
上面的查询将从
Order
与其相关的实体User
实体和返回的Order
对象将具有Order.user
属性预填充。它还可以用于自定义急切加载的集合中的条目;查询通常希望使用
Query.populate_existing()
假设父对象的主要集合可能已加载的方法::sess.query(User).\ join(User.addresses).\ filter(Address.email_address.like('%@aol.com')).\ options(contains_eager(User.addresses)).\ populate_existing()
请参阅部分 将显式联接/语句路由到热切加载的集合中 了解完整的用法详细信息。
参见
- function sqlalchemy.orm.defaultload(*keys)¶
指示应使用默认加载程序样式加载属性。
此方法用于将其他加载程序选项进一步链接到属性链中,而不改变链中链接的加载程序样式。例如,要为元素的某个元素设置连接的热切加载,请执行以下操作:
session.query(MyClass).options( defaultload(MyClass.someattribute). joinedload(MyOtherClass.someotherattribute) )
defaultload()
也可用于在相关类上设置列级选项,即defer()
和undefer()
::session.query(MyClass).options( defaultload(MyClass.someattribute). defer("some_column"). undefer("some_other_column") )
参见
Load.options()
-允许复杂的层次加载程序选项结构,其详细程度低于单个加载程序的详细程度。defaultload()
指令。
- function sqlalchemy.orm.eagerload(*args, **kwargs)¶
同义词
joinedload()
.1.4 版后已移除: 在SQLAlchemy的1.x系列中,AwardLoad构造被认为是遗留的,并将在2.0中删除。请使用
joinedload()
. (SQLAlchemy 2.0的背景: 迁移到Alchemy )
- function sqlalchemy.orm.immediateload(*keys)¶
指示应使用带有per attribute select语句的即时加载来加载给定的属性。
加载是使用“lazyloader”策略实现的,并且不会触发任何额外的急切加载程序。
这个
immediateload()
期权通常被selectinload()
选项,它通过为所有加载的对象发出一个选择来更有效地执行相同的任务。此函数是
Load
接口和支持方法链接和独立操作。参见
- function sqlalchemy.orm.joinedload(*keys, **kw)¶
指示应使用joined eager loading加载给定的属性。
此函数是
Load
接口和支持方法链接和独立操作。实例:
# joined-load the "orders" collection on "User" query(User).options(joinedload(User.orders)) # joined-load Order.items and then Item.keywords query(Order).options( joinedload(Order.items).joinedload(Item.keywords)) # lazily load Order.items, but when Items are loaded, # joined-load the keywords collection query(Order).options( lazyload(Order.items).joinedload(Item.keywords))
- 参数
innerjoin¶ -- 如果
True
,指示联接的热切加载应使用内部联接而不是默认的左外部联接::query(order).options(joined load(order.user,inner join=true)),以便将多个热切联接链接在一起,其中一些联接可能是外部联接,另一些联接可能是内部联接,右嵌套联接用于将它们链接::query(a).options(joined load(a.bs,innerjoin=假)。joinedLoad(b.cs,inner join=true))上述查询通过“outer”join链接a.bs,通过“inner”join链接b.cs,将使join呈现为“a left outer join(b join c)”。当使用旧版本的sqlite(<3.7.16)时,这种形式的join被转换为使用完整的子查询,因为这种语法不直接受支持。这个innerjoin
标志也可以用术语来表示"unnested"
. 这表示应使用内部联接, 除非 该联接链接到左侧的左侧外部联接,在这种情况下,它将呈现为左侧外部联接。例如,假设A.bs
是outerjoin::query(a).options(joinedload(a.bs)。joinedLoad(b.cs,innerjoin=“unnested”))上述联接将呈现为“左外部联接B左外部联接C”,而不是“左外部联接(b联接C)”。…注意:“unested”标志 not 影响从多对多关联表呈现的联接,例如配置为relationship.secondary
对于目标表;为了结果的正确性,这些联接始终是内部的,因此如果链接到外部联接,则是正确嵌套的。…版本已更改::1.0.0innerjoin=True
现在暗示innerjoin="nested"
,而在0.9中,它意味着innerjoin="unnested"
. 为了实现pre-1.0“untested”内部连接行为,请使用值innerjoin="unnested"
. 见 右内部联接嵌套现在是joinedLoad的默认值,inner join=true .
注解
产生的连接
joinedload()
是 匿名别名 . 不能修改联接进行的条件,也不能Query
以任何方式引用这些联接,包括排序。见 加入渴望装载的禅宗 更多细节。要生成显式可用的特定SQL联接,请使用
Query.join()
. 若要将显式联接与集合的急切加载结合起来,请使用contains_eager()
见 将显式联接/语句路由到热切加载的集合中 .参见
- class sqlalchemy.orm.Load(entity)¶
表示修改
Query
以影响各种映射属性的加载方式。这个
Load
在大多数情况下,当使用类似joinedload()
,defer()
或类似。然而,Load
对象也可以直接使用,在某些情况下是有用的。使用
Load
直接用目标映射类作为参数实例化它。这种用法在处理Query
有多个实体:myopt = Load(MyClass).joinedload("widgets")
以上
myopt
现在可用于Query.options()
,仅对MyClass
单位:session.query(MyClass, MyOtherClass).options(myopt)
其中一例
Load
当指定仅对特定类生效的“通配符”选项时,公共API非常有用:session.query(Order).options(Load(Order).lazyload('*'))
上面,所有关系
Order
将被延迟加载,但这些子对象上的其他属性将使用它们的常规加载策略进行加载。参见
类签名
class
sqlalchemy.orm.Load
(sqlalchemy.sql.expression.Generative
,sqlalchemy.orm.LoaderOption
)- method
sqlalchemy.orm.Load.
baked_lazyload(attr)¶ 产生新的
Load
对象与baked_lazyload()
选项应用。见
baked_lazyload()
用于示例。
- method
sqlalchemy.orm.Load.
contains_eager(attr, alias=None)¶ 产生新的
Load
对象与contains_eager()
选项应用。见
contains_eager()
用于示例。
- method
sqlalchemy.orm.Load.
defaultload(attr)¶ 产生新的
Load
对象与defaultload()
选项应用。见
defaultload()
用于示例。
- method
sqlalchemy.orm.Load.
defer(key, raiseload=False)¶ 见
defer()
用于示例。
- method
sqlalchemy.orm.Load.
immediateload(attr)¶ 产生新的
Load
对象与immediateload()
选项应用。见
immediateload()
用于示例。
- method
sqlalchemy.orm.Load.
joinedload(attr, innerjoin=None)¶ 产生新的
Load
对象与joinedload()
选项应用。见
joinedload()
用于示例。
- method
sqlalchemy.orm.Load.
lazyload(attr)¶ 产生新的
Load
对象与lazyload()
选项应用。见
lazyload()
用于示例。
- method
sqlalchemy.orm.Load.
load_only(*attrs)¶ 产生新的
Load
对象与load_only()
选项应用。见
load_only()
用于示例。
- method
sqlalchemy.orm.Load.
noload(attr)¶ 见
noload()
用于示例。
- method
sqlalchemy.orm.Load.
options(*opts)¶ 将一系列选项作为子选项应用于此
Load
对象。例如。::
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 新版功能.
参见
- method
sqlalchemy.orm.Load.
process_compile_state(compile_state)¶ 对给定的
CompileState
.
- method
sqlalchemy.orm.Load.
process_compile_state_replaced_entities(compile_state, mapper_entities)¶ 将修改应用于给定的
CompileState
,给定被替换为WITH_ONLY_COLUMNS()或WITH_ENTITIES()的实体。1.4.19 新版功能.
- method
sqlalchemy.orm.Load.
raiseload(attr, sql_only=False)¶ 产生新的
Load
对象与raiseload()
选项应用。见
raiseload()
用于示例。
- method
sqlalchemy.orm.Load.
selectin_polymorphic(classes)¶ 产生新的
Load
对象与selectin_polymorphic()
选项应用。见
selectin_polymorphic()
用于示例。
- method
sqlalchemy.orm.Load.
selectinload(attr)¶ 产生新的
Load
对象与selectinload()
选项应用。见
selectinload()
用于示例。
- method
sqlalchemy.orm.Load.
subqueryload(attr)¶ 产生新的
Load
对象与subqueryload()
选项应用。见
subqueryload()
用于示例。
- method
sqlalchemy.orm.Load.
undefer(key)¶ 见
undefer()
用于示例。
- method
sqlalchemy.orm.Load.
undefer_group(name)¶ 产生新的
Load
对象与undefer_group()
选项应用。见
undefer_group()
用于示例。
- method
sqlalchemy.orm.Load.
with_expression(key, expression)¶ 产生新的
Load
对象与with_expression()
选项应用。见
with_expression()
用于示例。
- method
- function sqlalchemy.orm.noload(*keys)¶
指示给定的关系属性应保持卸载状态。
关系属性将返回
None
当在不产生任何加载效果的情况下被访问时。此函数是
Load
接口和支持方法链接和独立操作。noload()
适用于relationship()
属性;有关基于列的属性,请参见defer()
.注解
属性将此加载策略设置为关系的默认策略。
relationship.lazy
参数可能会导致刷新问题,例如如果删除操作需要加载相关对象,而不是None
被退回了。参见
- function sqlalchemy.orm.raiseload(*keys, **kw)¶
指示给定属性在访问时应引发错误。
使用配置的关系属性
raiseload()
将提高InvalidRequestError
访问时。当应用程序试图确保在特定上下文中访问的所有关系属性都已通过预先加载加载时,这种方法非常有用。不必通过读取SQL日志来确保不发生延迟加载,此策略将导致它们立即提升。raiseload()
适用于relationship()
仅属性。若要将raise on SQL行为应用于基于列的属性,请使用defer.raiseload
上的参数defer()
装入程序选项。- 参数
sql_only¶ -- 如果为True,则仅当延迟加载将发出SQL时才会引发,但如果只是检查标识映射,或者由于缺少键而确定相关值应为None,则不应引发。当错误时,该策略将针对所有类型的关系加载进行提升。
此函数是
Load
接口和支持方法链接和独立操作。1.1 新版功能.
参见
- function sqlalchemy.orm.selectinload(*keys)¶
指示应使用select在预加载中加载给定的属性。
此函数是
Load
接口和支持方法链接和独立操作。实例:
# selectin-load the "orders" collection on "User" query(User).options(selectinload(User.orders)) # selectin-load Order.items and then Item.keywords query(Order).options( selectinload(Order.items).selectinload(Item.keywords)) # lazily load Order.items, but when Items are loaded, # selectin-load the keywords collection query(Order).options( lazyload(Order.items).selectinload(Item.keywords))
1.2 新版功能.
参见
- function sqlalchemy.orm.subqueryload(*keys)¶
指示应使用子查询预加载来加载给定的属性。
此函数是
Load
接口和支持方法链接和独立操作。实例:
# subquery-load the "orders" collection on "User" query(User).options(subqueryload(User.orders)) # subquery-load Order.items and then Item.keywords query(Order).options( subqueryload(Order.items).subqueryload(Item.keywords)) # lazily load Order.items, but when Items are loaded, # subquery-load the keywords collection query(Order).options( lazyload(Order.items).subqueryload(Item.keywords))
参见