SQLAlchemy 2.0 文档
- 上一页: ORM 启用的 INSERT、UPDATE 和 DELETE 语句
- 下一页: 关系加载技术
- 上级: 主页
- 本页内容
列加载选项¶
关于本文档
本节介绍了关于列加载的更多选项。使用的映射包含用于存储大型字符串值的列,我们可能希望限制何时加载它们。
查看本页的 ORM 设置。以下示例中,有些示例将重新定义 Book
映射器以修改一些列定义。
使用列延迟限制加载哪些列¶
列延迟 指的是在查询该类型的对象时从 SELECT 语句中省略的 ORM 映射列。这里的基本原理通常是性能,在表具有很少使用的列且这些列可能包含大型数据值的情况下,在每次查询中完全加载这些列可能会非常耗时且内存密集。SQLAlchemy ORM 提供了多种方法来控制在加载实体时加载列的方式。
本节中的大多数示例都说明了 ORM 加载器选项。这些是传递给 Select.options()
方法的 Select
对象的小构造,然后在将对象编译为 SQL 字符串时由 ORM 消耗。
使用 load_only()
减少加载的列¶
load_only()
加载器选项是在加载仅访问一小部分列的对象时最方便的选项。此选项接受数量可变的类绑定属性对象,这些对象指示应加载的那些列映射属性,其中除主键之外的所有其他列映射属性都不会成为获取的列的一部分。在以下示例中,Book
类包含列 .title
、.summary
和 .cover_photo
。使用 load_only()
,我们可以指示 ORM 仅在开始时加载 .title
和 .summary
列
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import load_only
>>> stmt = select(Book).options(load_only(Book.title, Book.summary))
>>> books = session.scalars(stmt).all()
SELECT book.id, book.title, book.summary
FROM book
[...] ()
>>> for book in books:
... print(f"{book.title} {book.summary}")
100 Years of Krabby Patties some long summary
Sea Catch 22 another long summary
The Sea Grapes of Wrath yet another summary
A Nut Like No Other some long summary
Geodesic Domes: A Retrospective another long summary
Rocketry for Squirrels yet another summary
上面,SELECT 语句省略了 .cover_photo
列,仅包含 .title
和 .summary
以及主键列 .id
;ORM 通常始终获取主键列,因为这些列是为行建立身份所必需的。
加载后,对象通常会对剩余未加载的属性应用 延迟加载 行为,这意味着当第一次访问任何属性时,将在当前事务内发出 SQL 语句以加载该值。下面,访问 .cover_photo
会发出 SELECT 语句以加载其值
>>> img_data = books[0].cover_photo
SELECT book.cover_photo AS book_cover_photo
FROM book
WHERE book.id = ?
[...] (1,)
延迟加载始终使用 Session
发出,该对象处于 持久 状态。如果对象与任何 Session
分离,则操作将失败并引发异常。
作为访问时延迟加载的替代方案,也可以将延迟列配置为在访问时引发信息性异常,无论其附加状态如何。当使用 load_only()
构造时,可以使用 load_only.raiseload
参数指示这一点。有关背景和示例,请参阅部分 使用 raiseload 阻止延迟列加载。
提示
如其他地方所述,在使用 异步 I/O (asyncio) 时,延迟加载不可用。
对多个实体使用 load_only()
¶
load_only()
将其自身限制为其属性列表中所指的单个实体(目前不允许传递跨越多个实体的属性列表)。在以下示例中,给定的 load_only()
选项仅适用于 Book
实体。所选的 User
实体不受影响;在生成的 SELECT 语句中,user_account
的所有列都存在,而 book
表中仅存在 book.id
和 book.title
>>> stmt = select(User, Book).join_from(User, Book).options(load_only(Book.title))
>>> print(stmt)
SELECT user_account.id, user_account.name, user_account.fullname,
book.id AS id_1, book.title
FROM user_account JOIN book ON user_account.id = book.owner_id
如果我们想要对 User
和 Book
都应用 load_only()
选项,我们将使用两个单独的选项
>>> stmt = (
... select(User, Book)
... .join_from(User, Book)
... .options(load_only(User.name), load_only(Book.title))
... )
>>> print(stmt)
SELECT user_account.id, user_account.name, book.id AS id_1, book.title
FROM user_account JOIN book ON user_account.id = book.owner_id
在相关对象和集合上使用 load_only()
¶
当使用 关系加载器 来控制相关对象的加载时,任何关系加载器的 Load.load_only()
方法可用于对子实体上的列应用 load_only()
规则。在以下示例中,selectinload()
用于加载每个 User
对象的相关 books
集合。通过对生成的选项对象应用 Load.load_only()
,当为关系加载对象时,发出的 SELECT 将仅引用 title
列以及主键列
>>> from sqlalchemy.orm import selectinload
>>> stmt = select(User).options(selectinload(User.books).load_only(Book.title))
>>> for user in session.scalars(stmt):
... print(f"{user.fullname} {[b.title for b in user.books]}")
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
[...] ()
SELECT book.owner_id AS book_owner_id, book.id AS book_id, book.title AS book_title
FROM book
WHERE book.owner_id IN (?, ?)
[...] (1, 2)
Spongebob Squarepants ['100 Years of Krabby Patties', 'Sea Catch 22', 'The Sea Grapes of Wrath']
Sandy Cheeks ['A Nut Like No Other', 'Geodesic Domes: A Retrospective', 'Rocketry for Squirrels']
load_only()
也可以应用于子实体,而无需指定关系本身要使用的加载方式。如果我们不想更改 User.books
的默认加载方式,但仍然要将 load only 规则应用于 Book
,则可以使用 defaultload()
选项,这种情况下将保留 User.books
的默认关系加载方式 “lazy”,并将自定义的 load_only()
规则应用于为每个 User.books
集合发出的 SELECT 语句。
>>> from sqlalchemy.orm import defaultload
>>> stmt = select(User).options(defaultload(User.books).load_only(Book.title))
>>> for user in session.scalars(stmt):
... print(f"{user.fullname} {[b.title for b in user.books]}")
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
[...] ()
SELECT book.id AS book_id, book.title AS book_title
FROM book
WHERE ? = book.owner_id
[...] (1,)
Spongebob Squarepants ['100 Years of Krabby Patties', 'Sea Catch 22', 'The Sea Grapes of Wrath']
SELECT book.id AS book_id, book.title AS book_title
FROM book
WHERE ? = book.owner_id
[...] (2,)
Sandy Cheeks ['A Nut Like No Other', 'Geodesic Domes: A Retrospective', 'Rocketry for Squirrels']
使用 defer()
省略特定列¶
defer()
加载器选项是 load_only()
的更细粒度的替代方案,它允许将单个特定列标记为“不要加载”。在下面的示例中,defer()
直接应用于 .cover_photo
列,保留所有其他列的行为不变。
>>> from sqlalchemy.orm import defer
>>> stmt = select(Book).where(Book.owner_id == 2).options(defer(Book.cover_photo))
>>> books = session.scalars(stmt).all()
SELECT book.id, book.owner_id, book.title, book.summary
FROM book
WHERE book.owner_id = ?
[...] (2,)
>>> for book in books:
... print(f"{book.title}: {book.summary}")
A Nut Like No Other: some long summary
Geodesic Domes: A Retrospective: another long summary
Rocketry for Squirrels: yet another summary
与 load_only()
一样,默认情况下,未加载的列将在使用 延迟加载 时进行加载。
>>> img_data = books[0].cover_photo
SELECT book.cover_photo AS book_cover_photo
FROM book
WHERE book.id = ?
[...] (4,)
可以在一个语句中使用多个 defer()
选项,以将多个列标记为延迟加载。
与 load_only()
一样,defer()
选项还包括在访问时让延迟属性引发异常而不是延迟加载的功能。这在 使用 raiseload 阻止延迟列加载 一节中进行了说明。
使用 raiseload 阻止延迟列加载¶
使用 load_only()
或 defer()
加载器选项时,在对象上标记为延迟加载的属性具有默认行为,即第一次访问时,将在当前事务中发出 SELECT 语句以加载其值。通常需要阻止此加载发生,而是在访问属性时引发异常,表明未预期需要查询数据库以获取此列。一个典型场景是操作在加载包含操作进行所需所有列的对象时,然后将这些对象传递到视图层。在视图层中发出的任何其他 SQL 操作都应被捕获,以便可以调整预加载操作以预先适应那些额外的数据,而不是产生额外的延迟加载。
对于这种情况,defer()
和 load_only()
选项包含一个布尔参数 defer.raiseload
,当设置为 True
时,将导致受影响的属性在访问时引发异常。在下面的示例中,延迟列 .cover_photo
将不允许属性访问。
>>> book = session.scalar(
... select(Book).options(defer(Book.cover_photo, raiseload=True)).where(Book.id == 4)
... )
SELECT book.id, book.owner_id, book.title, book.summary
FROM book
WHERE book.id = ?
[...] (4,)
>>> book.cover_photo
Traceback (most recent call last):
...
sqlalchemy.exc.InvalidRequestError: 'Book.cover_photo' is not available due to raiseload=True
使用 load_only()
指定一组特定的非延迟列时,可以使用 load_only.raiseload
参数将 raiseload
行为应用于剩余的列,该参数将应用于所有延迟属性。
>>> session.expunge_all()
>>> book = session.scalar(
... select(Book).options(load_only(Book.title, raiseload=True)).where(Book.id == 5)
... )
SELECT book.id, book.title
FROM book
WHERE book.id = ?
[...] (5,)
>>> book.summary
Traceback (most recent call last):
...
sqlalchemy.exc.InvalidRequestError: 'Book.summary' is not available due to raiseload=True
注意
目前无法在一个语句中混合引用同一实体的 load_only()
和 defer()
选项以更改某些属性的 raiseload
行为;目前,这样做会导致属性的加载行为不确定。
另请参阅
defer.raiseload
特性是关系中相同 “raiseload” 特性的列级版本。有关关系的 “raiseload”,请参阅本指南中 关系加载技术 部分的 使用 raiseload 阻止不需要的延迟加载 一节。
在映射上配置列延迟¶
defer()
的功能作为映射列的默认行为可用,这可能适用于不应在每个查询中无条件加载的列。要配置,请使用 mapped_column.deferred
参数 mapped_column()
。下面的示例说明了对 Book
的映射,它将默认列延迟应用于 summary
和 cover_photo
列。
>>> class Book(Base):
... __tablename__ = "book"
... id: Mapped[int] = mapped_column(primary_key=True)
... owner_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
... title: Mapped[str]
... summary: Mapped[str] = mapped_column(Text, deferred=True)
... cover_photo: Mapped[bytes] = mapped_column(LargeBinary, deferred=True)
...
... def __repr__(self) -> str:
... return f"Book(id={self.id!r}, title={self.title!r})"
使用上面的映射,对 Book
的查询将不会自动包含 summary
和 cover_photo
列。
>>> book = session.scalar(select(Book).where(Book.id == 2))
SELECT book.id, book.owner_id, book.title
FROM book
WHERE book.id = ?
[...] (2,)
与所有延迟一样,当首次访问加载对象上的延迟属性时,其默认行为是 延迟加载 其值。
>>> img_data = book.cover_photo
SELECT book.cover_photo AS book_cover_photo
FROM book
WHERE book.id = ?
[...] (2,)
与 defer()
和 load_only()
加载器选项一样,映射器级延迟还包括一个选项,如果在语句中没有其他选项存在,则会发生 raiseload
行为,而不是延迟加载。这允许一个映射,其中某些列默认情况下不会加载,并且在没有语句中使用的显式指令的情况下,也不会延迟加载。有关如何配置和使用此行为的背景信息,请参阅 配置映射器级 “raiseload” 行为 一节。
使用 deferred()
为命令式映射器映射 SQL 表达式¶
deferred()
函数是早期的、更通用的“延迟列”映射指令,它早于 SQLAlchemy 中引入 mapped_column()
构造。
deferred()
用于配置 ORM 映射器,并接受任意 SQL 表达式或 Column
对象。因此,它适合用于非声明式 命令式映射,将其传递给 map_imperatively.properties
字典。
from sqlalchemy import Blob
from sqlalchemy import Column
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy import Table
from sqlalchemy import Text
from sqlalchemy.orm import registry
mapper_registry = registry()
book_table = Table(
"book",
mapper_registry.metadata,
Column("id", Integer, primary_key=True),
Column("title", String(50)),
Column("summary", Text),
Column("cover_image", Blob),
)
class Book:
pass
mapper_registry.map_imperatively(
Book,
book_table,
properties={
"summary": deferred(book_table.c.summary),
"cover_image": deferred(book_table.c.cover_image),
},
)
deferred()
也可以用在 column_property()
的位置,当映射的 SQL 表达式应在延迟基础上加载时。
from sqlalchemy.orm import deferred
class User(Base):
__tablename__ = "user"
id: Mapped[int] = mapped_column(primary_key=True)
firstname: Mapped[str] = mapped_column()
lastname: Mapped[str] = mapped_column()
fullname: Mapped[str] = deferred(firstname + " " + lastname)
使用 undefer()
“急切地”加载延迟列¶
对于在映射上配置为默认延迟的列,undefer()
选项将导致任何通常延迟的列取消延迟,即与映射的所有其他列一起提前加载。例如,我们可以将 undefer()
应用于 Book.summary
列,该列在之前的映射中被指示为延迟。
>>> from sqlalchemy.orm import undefer
>>> book = session.scalar(select(Book).where(Book.id == 2).options(undefer(Book.summary)))
SELECT book.id, book.owner_id, book.title, book.summary
FROM book
WHERE book.id = ?
[...] (2,)
现在 Book.summary
列被急切加载,可以访问而无需发出额外的 SQL。
>>> print(book.summary)
another long summary
分组加载延迟列¶
通常,当一个列被映射为 mapped_column(deferred=True)
时,当在对象上访问延迟属性时,将发出 SQL 来只加载该特定列,而不会加载其他列,即使映射中有其他列也被标记为延迟。在延迟属性属于应该一起加载的一组属性的常见情况下,而不是为每个属性单独发出 SQL,可以使用 mapped_column.deferred_group
参数,它接受一个任意的字符串,该字符串将定义一个要取消延迟的公共列组。
>>> class Book(Base):
... __tablename__ = "book"
... id: Mapped[int] = mapped_column(primary_key=True)
... owner_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
... title: Mapped[str]
... summary: Mapped[str] = mapped_column(
... Text, deferred=True, deferred_group="book_attrs"
... )
... cover_photo: Mapped[bytes] = mapped_column(
... LargeBinary, deferred=True, deferred_group="book_attrs"
... )
...
... def __repr__(self) -> str:
... return f"Book(id={self.id!r}, title={self.title!r})"
使用上面的映射,访问 summary
或 cover_photo
将使用一个 SELECT 语句同时加载这两个列。
>>> book = session.scalar(select(Book).where(Book.id == 2))
SELECT book.id, book.owner_id, book.title
FROM book
WHERE book.id = ?
[...] (2,)
>>> img_data, summary = book.cover_photo, book.summary
SELECT book.summary AS book_summary, book.cover_photo AS book_cover_photo
FROM book
WHERE book.id = ?
[...] (2,)
使用 undefer_group()
按组取消延迟¶
如果延迟列使用 mapped_column.deferred_group
进行配置,如上一节介绍的那样,可以使用 undefer_group()
选项来指示整个组急切加载,并将要急切加载的组的字符串名称传递给它。
>>> from sqlalchemy.orm import undefer_group
>>> book = session.scalar(
... select(Book).where(Book.id == 2).options(undefer_group("book_attrs"))
... )
SELECT book.id, book.owner_id, book.title, book.summary, book.cover_photo
FROM book
WHERE book.id = ?
[...] (2,)
summary
和 cover_photo
都可以在没有额外加载的情况下使用。
>>> img_data, summary = book.cover_photo, book.summary
通配符上的取消延迟¶
大多数 ORM 加载程序选项都接受一个通配符表达式,用 "*"
表示,表示该选项应应用于所有相关属性。如果映射有一系列延迟列,可以使用通配符,一次取消延迟所有此类列,而无需使用组名。
>>> book = session.scalar(select(Book).where(Book.id == 3).options(undefer("*")))
SELECT book.id, book.owner_id, book.title, book.summary, book.cover_photo
FROM book
WHERE book.id = ?
[...] (3,)
配置映射器级别的“raiseload”行为¶
在 Using raiseload to prevent deferred column loads 中首次介绍的“raiseload”行为也可以作为默认的映射器级别的行为应用,使用 mapped_column.deferred_raiseload
参数 mapped_column()
。使用此参数时,受影响的列在所有情况下都将在访问时引发,除非在查询时使用 undefer()
或 load_only()
显式“取消延迟”。
>>> class Book(Base):
... __tablename__ = "book"
... id: Mapped[int] = mapped_column(primary_key=True)
... owner_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
... title: Mapped[str]
... summary: Mapped[str] = mapped_column(Text, deferred=True, deferred_raiseload=True)
... cover_photo: Mapped[bytes] = mapped_column(
... LargeBinary, deferred=True, deferred_raiseload=True
... )
...
... def __repr__(self) -> str:
... return f"Book(id={self.id!r}, title={self.title!r})"
使用上面的映射,.summary
和 .cover_photo
列默认不可加载。
>>> book = session.scalar(select(Book).where(Book.id == 2))
SELECT book.id, book.owner_id, book.title
FROM book
WHERE book.id = ?
[...] (2,)
>>> book.summary
Traceback (most recent call last):
...
sqlalchemy.exc.InvalidRequestError: 'Book.summary' is not available due to raiseload=True
只有在查询时覆盖它们的行为,通常使用 undefer()
或 undefer_group()
,或者不太常见地使用 defer()
,才能加载属性。下面的示例应用 undefer('*')
来取消延迟所有属性,也使用 Populate Existing 来刷新已加载对象的加载器选项。
>>> book = session.scalar(
... select(Book)
... .where(Book.id == 2)
... .options(undefer("*"))
... .execution_options(populate_existing=True)
... )
SELECT book.id, book.owner_id, book.title, book.summary, book.cover_photo
FROM book
WHERE book.id = ?
[...] (2,)
>>> book.summary
'another long summary'
将任意 SQL 表达式加载到对象上¶
如 Selecting ORM Entities and Attributes 和其他地方所述,select()
结构可用于在结果集中加载任意 SQL 表达式。例如,如果我们想要发出一个查询来加载 User
对象,但还包括每个 User
所拥有的书籍数量,我们可以使用 func.count(Book.id)
将一个“count”列添加到包含一个 JOIN 到 Book
以及一个 GROUP BY 拥有者 ID 的查询中。这将产生 Row
对象,每个对象包含两个条目,一个用于 User
,一个用于 func.count(Book.id)
。
>>> from sqlalchemy import func
>>> stmt = select(User, func.count(Book.id)).join_from(User, Book).group_by(Book.owner_id)
>>> for user, book_count in session.execute(stmt):
... print(f"Username: {user.name} Number of books: {book_count}")
SELECT user_account.id, user_account.name, user_account.fullname,
count(book.id) AS count_1
FROM user_account JOIN book ON user_account.id = book.owner_id
GROUP BY book.owner_id
[...] ()
Username: spongebob Number of books: 3
Username: sandy Number of books: 3
在上面的示例中,User
实体和“书籍数量”SQL 表达式是分开返回的。但是,一个常见的用例是产生一个查询,该查询将产生仅 User
对象,例如可以使用 Session.scalars()
进行迭代,其中 func.count(Book.id)
SQL 表达式的结果被动态应用于每个 User
实体。最终结果类似于使用 column_property()
将任意 SQL 表达式映射到类的情况,只是 SQL 表达式可以在查询时修改。对于这种情况,SQLAlchemy 提供了 with_expression()
加载器选项,当与映射器级别的 query_expression()
指令结合使用时,可以产生此结果。
要将 with_expression()
应用于查询,映射的类必须使用 query_expression()
指令预先配置了一个 ORM 映射属性;此指令将在映射的类上生成一个适合接收查询时 SQL 表达式的属性。在下面,我们向 User
添加一个新的属性 User.book_count
。此 ORM 映射属性是只读的,没有默认值;在加载的实例上访问它通常会产生 None
。
>>> from sqlalchemy.orm import query_expression
>>> class User(Base):
... __tablename__ = "user_account"
... id: Mapped[int] = mapped_column(primary_key=True)
... name: Mapped[str]
... fullname: Mapped[Optional[str]]
... book_count: Mapped[int] = query_expression()
...
... def __repr__(self) -> str:
... return f"User(id={self.id!r}, name={self.name!r}, fullname={self.fullname!r})"
在我们的映射中配置了 User.book_count
属性后,我们可以使用 with_expression()
加载器选项来用 SQL 表达式中的数据填充它,以便在加载时将自定义 SQL 表达式应用于每个 User
对象。
>>> from sqlalchemy.orm import with_expression
>>> stmt = (
... select(User)
... .join_from(User, Book)
... .group_by(Book.owner_id)
... .options(with_expression(User.book_count, func.count(Book.id)))
... )
>>> for user in session.scalars(stmt):
... print(f"Username: {user.name} Number of books: {user.book_count}")
SELECT count(book.id) AS count_1, user_account.id, user_account.name,
user_account.fullname
FROM user_account JOIN book ON user_account.id = book.owner_id
GROUP BY book.owner_id
[...] ()
Username: spongebob Number of books: 3
Username: sandy Number of books: 3
在上面,我们将 func.count(Book.id)
表达式从 select()
结构的 columns 参数移动到 with_expression()
加载器选项中。然后 ORM 将其视为一个特殊的列加载选项,该选项被动态地应用于语句。
query_expression()
映射有以下注意事项。
在没有使用
with_expression()
来填充属性的对象上,对象实例上的属性将具有值None
,除非在映射上设置了query_expression.default_expr
参数以设置为默认 SQL 表达式。with_expression()
值不会填充到已经加载的对象上,除非使用 Populate Existing。下面的示例将不起作用,因为A
对象已经加载。# load the first A obj = session.scalars(select(A).order_by(A.id)).first() # load the same A with an option; expression will **not** be applied # to the already-loaded object obj = session.scalars(select(A).options(with_expression(A.expr, some_expr))).first()
要确保在现有对象上重新加载属性,请使用 Populate Existing 执行选项以确保所有列都被重新填充。
obj = session.scalars( select(A) .options(with_expression(A.expr, some_expr)) .execution_options(populate_existing=True) ).first()
with_expression()
SQL 表达式在对象过期时会丢失。一旦对象过期,无论是通过Session.expire()
还是通过Session.commit()
的 expire_on_commit 行为,SQL 表达式及其值不再与属性相关联,并且将在后续访问时返回None
。with_expression()
作为对象加载选项,只对查询的最外层有效,并且只对针对完整实体的查询有效,而不能用于任意列选择、子查询或复合语句(如 UNION)的元素。有关示例,请参见下一节 Using with_expression() with UNIONs, other subqueries。映射的属性不能应用于查询的其他部分,例如 WHERE 子句、ORDER BY 子句,并使用 ad-hoc 表达式;也就是说,这将不起作用。
# can't refer to A.expr elsewhere in the query stmt = ( select(A) .options(with_expression(A.expr, A.x + A.y)) .filter(A.expr > 5) .order_by(A.expr) )
在上面的 WHERE 子句和 ORDER BY 子句中,
A.expr
表达式将解析为 NULL。为了在整个查询中使用该表达式,请将其分配给一个变量并使用该变量# assign desired expression up front, then refer to that in # the query a_expr = A.x + A.y stmt = ( select(A) .options(with_expression(A.expr, a_expr)) .filter(a_expr > 5) .order_by(a_expr) )
另请参阅
with_expression()
选项是用于在查询时动态地将 SQL 表达式应用于映射类的一种特殊选项。有关在映射器上配置的普通固定 SQL 表达式,请参见 SQL 表达式作为映射属性 部分。
在 UNION、其他子查询中使用 with_expression()
¶
with_expression()
结构体是一个 ORM 加载选项,因此只能应用于要加载特定 ORM 实体的 SELECT 语句的最外层。如果在将用作子查询或用作复合语句(如 UNION)中的元素的 select()
中使用,它不会有任何效果。
为了在子查询中使用任意 SQL 表达式,应使用添加表达式的正常 Core 风格方法。要将子查询派生的表达式组装到 ORM 实体的 query_expression()
属性上,with_expression()
用于 ORM 对象加载的顶层,引用子查询中的 SQL 表达式。
在下面的示例中,两个 select()
结构体用于针对 ORM 实体 A
,其中包含用 expr
标记的额外 SQL 表达式,并使用 union_all()
组合。然后,在最顶层,A
实体从这个 UNION 中进行 SELECT,使用在 从 UNION 和其他集合操作中选择实体 中描述的查询技术,添加一个带有 with_expression()
的选项来将这个 SQL 表达式提取到新加载的 A
实例上
>>> from sqlalchemy import union_all
>>> s1 = (
... select(User, func.count(Book.id).label("book_count"))
... .join_from(User, Book)
... .where(User.name == "spongebob")
... )
>>> s2 = (
... select(User, func.count(Book.id).label("book_count"))
... .join_from(User, Book)
... .where(User.name == "sandy")
... )
>>> union_stmt = union_all(s1, s2)
>>> orm_stmt = (
... select(User)
... .from_statement(union_stmt)
... .options(with_expression(User.book_count, union_stmt.selected_columns.book_count))
... )
>>> for user in session.scalars(orm_stmt):
... print(f"Username: {user.name} Number of books: {user.book_count}")
SELECT user_account.id, user_account.name, user_account.fullname, count(book.id) AS book_count
FROM user_account JOIN book ON user_account.id = book.owner_id
WHERE user_account.name = ?
UNION ALL
SELECT user_account.id, user_account.name, user_account.fullname, count(book.id) AS book_count
FROM user_account JOIN book ON user_account.id = book.owner_id
WHERE user_account.name = ?
[...] ('spongebob', 'sandy')
Username: spongebob Number of books: 3
Username: sandy Number of books: 3
列加载 API¶
对象名称 | 描述 |
---|---|
defer(key, *addl_attrs, [raiseload]) |
指示应推迟加载给定的面向列的属性,例如,在访问之前不加载。 |
deferred(column, *additional_columns, [group, raiseload, comparator_factory, init, repr, default, default_factory, compare, kw_only, hash, active_history, expire_on_flush, info, doc]) |
指示默认情况下不会加载的基于列的映射属性,除非访问它。 |
load_only(*attrs, [raiseload]) |
指示对于特定实体,应加载给定的基于列的属性名称列表;所有其他属性将被推迟。 |
query_expression([default_expr], *, [repr, compare, expire_on_flush, info, doc]) |
指示从查询时 SQL 表达式中填充的属性。 |
undefer(key, *addl_attrs) |
指示应取消推迟给定的面向列的属性,例如,在实体的 SELECT 语句中指定。 |
undefer_group(name) |
指示应取消推迟给定推迟组名称中的列。 |
with_expression(key, expression) |
将临时 SQL 表达式应用于“推迟表达式”属性。 |
- function sqlalchemy.orm.defer(key: Literal['*'] | QueryableAttribute[Any], *addl_attrs: Literal['*'] | QueryableAttribute[Any], raiseload: bool = False) → _AbstractLoad¶
指示应推迟加载给定的面向列的属性,例如,在访问之前不加载。
此函数是
Load
接口的一部分,支持方法链和独立操作。例如:
from sqlalchemy.orm import defer session.query(MyClass).options( defer(MyClass.attribute_one), defer(MyClass.attribute_two) )
要指定相关类中属性的推迟加载,可以逐个指定路径,指定链中每个链接的加载样式。要将链接的加载样式保持不变,请使用
defaultload()
session.query(MyClass).options( defaultload(MyClass.someattr).defer(RelatedClass.some_column) )
多个与关系相关的推迟选项可以使用
Load.options()
组合在一起。select(MyClass).options( defaultload(MyClass.someattr).options( defer(RelatedClass.some_column), defer(RelatedClass.some_other_column), defer(RelatedClass.another_column) ) )
- 参数:
key¶ – 要推迟的属性。
raiseload¶ – 当访问推迟属性时,引发
InvalidRequestError
,而不是延迟加载值。用于防止发出不必要的 SQL。
版本 1.4 中的新增内容。
- function sqlalchemy.orm.deferred(column: _ORMColumnExprArgument[_T], *additional_columns: _ORMColumnExprArgument[Any], group: str | None = None, raiseload: bool = False, comparator_factory: Type[PropComparator[_T]] | None = None, init: _NoArg | bool = _NoArg.NO_ARG, repr: _NoArg | bool = _NoArg.NO_ARG, default: Any | None = _NoArg.NO_ARG, default_factory: _NoArg | Callable[[], _T] = _NoArg.NO_ARG, compare: _NoArg | bool = _NoArg.NO_ARG, kw_only: _NoArg | bool = _NoArg.NO_ARG, hash: _NoArg | bool | None = _NoArg.NO_ARG, active_history: bool = False, expire_on_flush: bool = True, info: _InfoType | None = None, doc: str | None = None) → MappedSQLExpression[_T]¶
指示默认情况下不会加载的基于列的映射属性,除非访问它。
当使用
mapped_column()
时,与deferred()
构造相同的函数通过使用mapped_column.deferred
参数提供。- 参数:
其他参数与
column_property()
中的参数相同。
- function sqlalchemy.orm.query_expression(default_expr: _ORMColumnExprArgument[_T] = <sqlalchemy.sql.elements.Null object>, *, repr: Union[_NoArg, bool] = _NoArg.NO_ARG, compare: Union[_NoArg, bool] = _NoArg.NO_ARG, expire_on_flush: bool = True, info: Optional[_InfoType] = None, doc: Optional[str] = None) → MappedSQLExpression[_T]¶
指示从查询时 SQL 表达式中填充的属性。
- 参数:
default_expr¶ – 可选的 SQL 表达式对象,如果以后没有使用
with_expression()
赋值,则将在所有情况下使用。
新版本 1.2。
另请参阅
在对象上加载任意 SQL 表达式 - 背景和使用示例
- function sqlalchemy.orm.load_only(*attrs: Literal['*'] | QueryableAttribute[Any], raiseload: bool = False) → _AbstractLoad¶
指示对于特定实体,应加载给定的基于列的属性名称列表;所有其他属性将被推迟。
此函数是
Load
接口的一部分,支持方法链和独立操作。示例 - 给定一个类
User
,仅加载name
和fullname
属性session.query(User).options(load_only(User.name, User.fullname))
示例 - 给定一个关系
User.addresses -> Address
,指定User.addresses
集合的子查询加载,但在每个Address
对象上仅加载email_address
属性session.query(User).options( subqueryload(User.addresses).load_only(Address.email_address) )
对于包含多个实体的语句,可以使用
Load
构造函数专门引用主实体stmt = ( select(User, Address) .join(User.addresses) .options( Load(User).load_only(User.name, User.fullname), Load(Address).load_only(Address.email_address), ) )
当与 populate_existing 执行选项一起使用时,只会刷新列出的属性。
- 参数:
*attrs¶ – 要加载的属性,所有其他属性将被延迟。
raiseload¶ –
当访问延迟属性时,引发
InvalidRequestError
而不是延迟加载值。用于防止发出不需要的 SQL。版本 2.0 中的新增功能。
另请参阅
使用列推迟限制哪些列加载 - 在 ORM 查询指南 中
- 参数:
*attrs¶ – 要加载的属性,所有其他属性将被延迟。
raiseload¶ –
当访问延迟属性时,引发
InvalidRequestError
而不是延迟加载值。用于防止发出不需要的 SQL。版本 2.0 中的新增功能。
- function sqlalchemy.orm.undefer(key: Literal['*'] | QueryableAttribute[Any], *addl_attrs: Literal['*'] | QueryableAttribute[Any]) → _AbstractLoad¶
指示应取消推迟给定的面向列的属性,例如,在实体的 SELECT 语句中指定。
通常将被取消延迟的列设置为映射上的
deferred()
属性。此函数是
Load
接口的一部分,支持方法链和独立操作。示例
# undefer two columns session.query(MyClass).options( undefer(MyClass.col1), undefer(MyClass.col2) ) # undefer all columns specific to a single class using Load + * session.query(MyClass, MyOtherClass).options( Load(MyClass).undefer("*") ) # undefer a column on a related object select(MyClass).options( defaultload(MyClass.items).undefer(MyClass.text) )
- 参数:
key¶ – 要取消延迟的属性。
- function sqlalchemy.orm.undefer_group(name: str) → _AbstractLoad¶
指示应取消推迟给定推迟组名称中的列。
被取消延迟的列在映射上被设置为
deferred()
属性,并且包含一个“组”名称。例如
session.query(MyClass).options(undefer_group("large_attrs"))
要取消延迟相关实体上的一组属性,可以使用关系加载器选项拼写出路径,例如
defaultload()
select(MyClass).options( defaultload("someattr").undefer_group("large_attrs") )
- function sqlalchemy.orm.with_expression(key: _AttrType, expression: _ColumnExpressionArgument[Any]) → _AbstractLoad¶
将临时 SQL 表达式应用于“推迟表达式”属性。
此选项与
query_expression()
映射级构造一起使用,该构造指示应成为临时 SQL 表达式的目标的属性。例如
stmt = select(SomeClass).options( with_expression(SomeClass.x_y_expr, SomeClass.x + SomeClass.y) )
新版本 1.2。
另请参阅
在对象上加载任意 SQL 表达式 - 背景和使用示例
flambé! 龙和 炼金术士 图像设计由 Rotem Yaari 创建并慷慨捐赠。
使用 Sphinx 7.2.6 创建。最后生成文档:Fri 08 Nov 2024 08:41:19 AM EST