Column Loading Options¶
About this Document
この項では、列のロードに関する追加オプションを示します。使用されるマッピングには、ロード時に制限する必要がある大きな文字列値を格納する列が含まれます。
View the ORM setup for this page の以下のいくつかの例では、列定義のいくつかを変更するために Book
マッパーを再定義します。
Limiting which Columns Load with Column Deferral¶
列の遅延 は、その型のオブジェクトがクエリされるときにSELECTステートメントから省略されるORMマップ列を参照します。ここでの一般的な理論的根拠は、テーブルに潜在的に大きなデータ値を持つめったに使用されない列がある場合のパフォーマンスです。これらの列をクエリごとに完全にロードすると、時間やメモリを大量に消費する可能性があるためです。SQLAlchemy ORMは、エンティティがロードされるときに列のロードを制御するさまざまな方法を提供します。
このセクションのほとんどの例は、 ORMローダオプション を示しています。これらは Select
オブジェクトの Select.options()
メソッドに渡される小さな構成体で、オブジェクトがSQL文字列にコンパイルされるときにORMによって消費されます。
Using load_only()
to reduce loaded columns¶
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は通常、行のIDを確立するために必要な主キー列をフェッチします。
一度ロードされると、オブジェクトは通常、残りのアンロードされた属性に適用される lazy loading 動作を持ちます。つまり、いずれかが最初にアクセスされると、値をロードするために現在のトランザクション内でSQL文が発行されます。以下では、 .cover_photo
にアクセスすると、値をロードするためのSELECT文が発行されます:
>>> img_data = books[0].cover_photo
SELECT book.cover_photo AS book_cover_photo
FROM book
WHERE book.id = ?
[...] (1,)
遅延ロードは常に、オブジェクトが persistent 状態にある Session
を使用して発行されます。オブジェクトが Session
から detached の場合、操作は失敗し、例外が発生します。
遅延列は、アクセス時の遅延読み込みの代わりに、接続状態に関係なく、アクセス時に情報例外を発生するように設定することもできます。 load_only()
構文を使用する場合、これは load_only.raiseload
パラメータを使用して示すことができます。背景と例については Using raiseload to prevent deferred column loads の節を参照してください。
Tip
as noted elsewhere, lazy loading is not available when using Asynchronous I/O (asyncio).
Using load_only()
with multiple entities¶
load_only()
limits itself to the single entity that is referred towards in its list of attributes (passing a list of attributes that span more than a single entity is currently disallowed). In the example below, the given load_only()
option applies only to the Book
entity. The User
entity that’s also selected is not affected; within the resulting SELECT statement, all columns for user_account
are present, whereas only book.id
and book.title
are present for the book
table:
:func:`_orm.load_only` は、属性のリストで参照されている単一のエンティティに自身を制限します(現在、複数のエンティティにまたがる属性のリストを渡すことは禁止されています)。以下の例では、与えられた :func:`_orm.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)
{printsql}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
load_only()
オプションを User
と Book
の両方に適用したければ、2つの別々のオプションを使うことになります:
>>> 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
Using defer()
to omit specific columns¶
defer()
ローダオプションは、 load_only()
のより細かい代替で、単一の特定の列を”読み込まない”とマークすることができます。以下の例では、 defer()
は直接e .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()
の場合と同様に、デフォルトでアンロードされた列は、 lazy loading を使ってアクセスされた時に自身をロードします:
>>> img_data = books[0].cover_photo
SELECT book.cover_photo AS book_cover_photo
FROM book
WHERE book.id = ?
[...] (4,)
1つの文で複数の defer()
オプションを使用して、複数の列をdeferredとしてマークすることができます。
load_only()
の場合と同様に、 defer()
オプションには、遅延読み込みではなく、アクセス時に遅延属性に例外を発生させる機能も含まれています。これについては、 Using raiseload to prevent deferred column loads の節で説明しています。
Using raiseload to prevent deferred column loads¶
load_only()
または defer()
ローダオプションを使用する場合、オブジェクトでdeferredとマークされた属性は、最初にアクセスされたときに、その値をロードするために現在のトランザクション内でSELECT文が発行されるというデフォルトの動作を持ちます。多くの場合、このロードが発生しないようにする必要があります。代わりに、このカラムに対してデータベースにクエリを実行する必要がないことを示す例外が、アトリビュートがアクセスされたときに発生します。一般的なシナリオは、操作を続行するために必要であることがわかっているすべてのカラムとともにオブジェクトがロードされ、ビューレイヤに渡される操作です。ビューレイヤ内で発行されるその他のSQL操作は、追加の遅延ロードを発生させるのではなく、事前にその追加データに対応するようにアップフロントローディング操作を調整できるように、キャッチされる必要があります。
このユースケースでは、 defer()
オプションと load_only()
オプションにはブールパラメータ defer.raiseload
が含まれています。これを True
に設定すると、影響を受ける属性がアクセス時に発生します。以下の例では、deferred列 .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()
を使って非deferred列の特定のセットに名前を付ける場合、 load_only.raiseload
パラメータを使って残りの列に raiseload
の振る舞いを適用することができます。これはすべてのdeferred属性に適用されます:
>>> 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
Note
同じエンティティを参照する:func:_orm.load_only`オプションと:func:`_orm.defer`オプションを1つの文の中に混在させて、特定の属性の ``raiseload` の動作を変更することはまだできません。現在のところ、これを行うと、属性のロード動作が未定義になります。
See also
defer.raiseload
機能は、関係で使用可能な同じ”raiseload”機能の列レベルバージョンです。関係での”raiseload”については、このガイドの Relationship Loading Techniques セクションの Preventing unwanted lazy loads using raiseload を 参照してください。
Configuring Column Deferral on Mappings¶
defer()
の機能は、マップされた列のデフォルトの動作として利用できます。これは、すべての問い合わせで無条件にロードされるべきではない列に適しています。設定するには、 mapped_column()
の mapped_column.deferred
パラメータを使用してください。以下の例は、デフォルトの列の遅延を summary
列と cover_photo
列に適用する Book
のマッピングを示しています。
>>> 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,)
すべての遅延の場合と同様に、ロードされたオブジェクトのdeferred属性が最初にアクセスされたときのデフォルトの動作は、その値を lazy load します:
>>> img_data = book.cover_photo
SELECT book.cover_photo AS book_cover_photo
FROM book
WHERE book.id = ?
[...] (2,)
defer()
および load_only()
ローダオプションの場合と同様に、マッパーレベルの遅延には、他のオプションが文に存在しない場合に、遅延ロードではなく、 raiseload
動作を発生させるためのオプションも含まれています。これにより、特定の列がデフォルトでロードされず、文で明示的な指示が使用されない限り遅延ロードされないマッピングが可能になります。この動作を設定して使用する方法の背景については、 Configuring mapper-level “raiseload” behavior を参照してください。
Using deferred()
for imperative mappers, mapped SQL expressions¶
deferred()
関数は、SQLAlchemyでの mapped_column()
構文の導入よりも前の、より汎用的な”deferred column”マッピング指示子です。
deferred()
はORMマッパーを設定する際に使用され、任意のSQL式または Column
オブジェクトを受け付けます。そのため、非宣言的な imperative mappings と一緒に使用するのに適しており、それを map_imperative.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()
は、マップされたSQL式を遅延ベースでロードする必要がある場合に、 column_property()
の代わりに使用することもできます。:
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)
See also
Using column_property - in the section SQL Expressions as Mapped Attributes
Applying Load, Persistence and Mapping Options for Imperative Table Columns - in the section Table Configuration with Declarative
Using undefer()
to “eagerly” load deferred columns¶
デフォルトでマッピングの列がdeferに設定されている場合、 undefer()
オプションを指定すると、通常deferされている列はすべてdefer解除されます。つまり、マッピングの他のすべての列とともに先にロードされます。例えば、 undefer()
を、前のマッピングでdeferredと示されている Book.summary
列に適用できます。
>>> from sqlalchemy.orm import undefer
>>> book = session.scalar(select(Book).where(Book.id == 2).options(undefer(Book.summary)))
{execsql}SELECT book.id, book.owner_id, book.title, book.summary
FROM book
WHERE book.id = ?
[...] (2,)
Loading deferred columns in groups¶
通常、カラムが mapped_column(deferred=True)
でマップされている場合、deferred属性がオブジェクトにアクセスされると、マッピングにdeferredとマークされた他のカラムがあっても、SQLはその特定のカラムだけをロードし、他のカラムはロードしないように発行されます。deferred属性が、各属性に対して個別にSQLを発行するのではなく、すべてを一度にロードする必要がある属性のグループの一部である一般的なケースでは、 mapped_column.deferred_group
パラメータを使用できます。これは、deferredされない共通のカラムのグループを定義する任意の文字列を受け入れます:
>>> 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
のいずれかにアクセスすると、1つの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,)
Undeferring by group with undefer_group()
¶
前のセクションで紹介したように、deferred列が 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
Undeferring on wildcards¶
ほとんどの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,)
Configuring mapper-level “raiseload” behavior¶
Using raiseload to prevent deferred column loads で最初に導入された”raiseload”動作は、 mapped_column()
の mapped_column.deferred_raiseload
パラメータを使用して、デフォルトのマッパーレベルの動作として適用することもできます。このパラメータを使用すると、クエリ時に undefer()
または load_only()
を使用して明示的に”undeferred”しない限り、影響を受ける列はすべての場合にアクセス時に発生します。:
>>> 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するために 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'
Loading Arbitrary SQL Expressions onto Objects¶
Selecting ORM Entities and Attributes などで説明されているように、 select()
構文は結果セットに任意のSQL式をロードするために使うことができます。たとえば、 User
オブジェクトをロードするだけでなく、 User
が所有する本の数も含むクエリを発行したい場合、 func.count(Book.id)
を使って、 Book
へのJOINとGROUP BY owner idを含むクエリに count
列を追加できます。これにより、それぞれ2つのエントリを含む Row
オブジェクトが生成されます。1つは User
用、もう1つは 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}")
{execsql}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
[...] ()
{stop}Username: spongebob Number of books: 3
Username: sandy Number of books: 3
上記の例では、 User
エンティティと book count
SQL式は別々に返されます。しかし、一般的なユースケースは、 User
オブジェクトのみを生成するクエリを生成することです。これは、例えば Session.scalars()
を使って繰り返すことができます。ここで、 func.count(Book.id)
SQL式の結果は、各 User
エンティティに*動的に*適用されます。最終的な結果は、任意のSQL式が column_property()
を使ってクラスにマップされた場合と似ていますが、SQL式はクエリ時に変更できます。このユースケースでは、SQLAlchemyは with_expression()
ローダオプションを提供します。これをマッパーレベルの query_expression()
ディレクティブと組み合わせると、この結果が生成される可能性があります。
with_expression()
をクエリに適用するには、マップされたクラスは query_expression()
ディレクティブを使ってORMマップされた属性を事前に設定しておく必要があります。このディレクティブは、クエリ時のSQL式を受け取るのに適したマップされたクラスの属性を生成します。以下では、新しい属性``User.book_count``を``User``に追加します。この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
属性を使って、ロード時にカスタムSQL式を各 User
オブジェクトに適用するために、 with_expression()
ローダオプションを使ってSQL式からデータを読み込むことができます:
>>> 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()
マッピングには以下の注意事項があります。:
On an object where
with_expression()
were not used to populate the attribute, the attribute on an object instance will have the valueNone
, unless on the mapping thequery_expression.default_expr
parameter is set to a default SQL expression.属性の設定に
with_expression()
が使われていないオブジェクトでは、オブジェクトインスタンスの属性の値はNone
になります。ただし、マッピングでquery_expression.default_expr
パラメータがデフォルトのSQL式に設定されている場合を除きます。The
with_expression()
value does not populate on an object that is already loaded, unless Populate Existing is used. The example below will not work, as theA
object is already loaded:Populate Existing が使用されない限り、
with_expression()
の値はすでにロードされているオブジェクトには設定されません 。次の例は、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()
To ensure the attribute is re-loaded on an existing object, use the Populate Existing execution option to ensure all columns are re-populated:
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句など)に 適用できず 、アドホック式を使用できません。つまり、次のようには機能しません。:
# 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) )
See also
with_expression()
オプションは、問い合わせ時に動的にSQL式をマップされたクラスに適用するために使用される特別なオプションです。マッパーで設定される通常の固定SQL式については、 SQL Expressions as Mapped Attributes を参照してください。
Using with_expression()
with UNIONs, other subqueries¶
with_expression()
構文はORMローダオプションなので、特定のORMエンティティをロードするSELECT文の最も外側のレベルにのみ適用できます。 select()
の内部で使用した場合は何の効果もありません。 select()
は副問い合わせやUNIONなどの複合文の要素として使用されます。
サブクエリで任意のSQL式を使用するには、通常のCoreスタイルの式を追加する方法を使用する必要があります。サブクエリから派生した式をORMエンティティの query_expression()
属性にアセンブルするには、 with_expression()
をORMオブジェクト読み込みの最上位層で使用し、サブクエリ内のSQL式を参照します。
以下の例では、2つの select()
構文がORMエンティティ A
に対して、追加のSQL式が expr
でラベル付けされて使用され、 union_all()
を使用して結合されます。次に、一番上の層で、 Selecting Entities from UNIONs and other set operations で説明されている問い合わせテクニックを使用して、このUNIONから A
エンティティが選択され、 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
Column Loading API¶
Object Name | Description |
---|---|
defer(key, *addl_attrs, [raiseload]) |
Indicate that the given column-oriented attribute should be deferred, e.g. not loaded until accessed. |
deferred(column, *additional_columns, [group, raiseload, comparator_factory, init, repr, default, default_factory, compare, kw_only, active_history, expire_on_flush, info, doc]) |
Indicate a column-based mapped attribute that by default will not load unless accessed. |
load_only(*attrs, [raiseload]) |
Indicate that for a particular entity, only the given list of column-based attribute names should be loaded; all others will be deferred. |
query_expression([default_expr], *, [repr, compare, expire_on_flush, info, doc]) |
Indicate an attribute that populates from a query-time SQL expression. |
undefer(key, *addl_attrs) |
Indicate that the given column-oriented attribute should be undeferred, e.g. specified within the SELECT statement of the entity as a whole. |
undefer_group(name) |
Indicate that columns within the given deferred group name should be undeferred. |
with_expression(key, expression) |
Apply an ad-hoc SQL expression to a “deferred expression” attribute. |
- function sqlalchemy.orm.defer(key: Literal['*'] | QueryableAttribute[Any], *addl_attrs: Literal['*'] | QueryableAttribute[Any], raiseload: bool = False) _AbstractLoad ¶
Indicate that the given column-oriented attribute should be deferred, e.g. not loaded until accessed.
This function is part of the
Load
interface and supports both method-chained and standalone operation.e.g.:
from sqlalchemy.orm import defer session.query(MyClass).options( defer(MyClass.attribute_one), defer(MyClass.attribute_two) )
To specify a deferred load of an attribute on a related class, the path can be specified one token at a time, specifying the loading style for each link along the chain. To leave the loading style for a link unchanged, use
defaultload()
:session.query(MyClass).options( defaultload(MyClass.someattr).defer(RelatedClass.some_column) )
Multiple deferral options related to a relationship can be bundled at once using
Load.options()
:select(MyClass).options( defaultload(MyClass.someattr).options( defer(RelatedClass.some_column), defer(RelatedClass.some_other_column), defer(RelatedClass.another_column) ) )
- Parameters:
key¶ – Attribute to be deferred.
raiseload¶ – raise
InvalidRequestError
rather than lazy loading a value when the deferred attribute is accessed. Used to prevent unwanted SQL from being emitted.
New in version 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, active_history: bool = False, expire_on_flush: bool = True, info: _InfoType | None = None, doc: str | None = None) MappedSQLExpression[_T] ¶
Indicate a column-based mapped attribute that by default will not load unless accessed.
When using
mapped_column()
, the same functionality as that ofdeferred()
construct is provided by using themapped_column.deferred
parameter.- Parameters:
*columns¶ – columns to be mapped. This is typically a single
Column
object, however a collection is supported in order to support multiple columns mapped under the same attribute.raiseload¶ –
boolean, if True, indicates an exception should be raised if the load operation is to take place.
New in version 1.4.
Additional arguments are the same as that of
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] ¶
Indicate an attribute that populates from a query-time SQL expression.
- Parameters:
default_expr¶ – Optional SQL expression object that will be used in all cases if not assigned later with
with_expression()
.
New in version 1.2.
See also
Loading Arbitrary SQL Expressions onto Objects - background and usage examples
- function sqlalchemy.orm.load_only(*attrs: Literal['*'] | QueryableAttribute[Any], raiseload: bool = False) _AbstractLoad ¶
Indicate that for a particular entity, only the given list of column-based attribute names should be loaded; all others will be deferred.
This function is part of the
Load
interface and supports both method-chained and standalone operation.Example - given a class
User
, load only thename
andfullname
attributes:session.query(User).options(load_only(User.name, User.fullname))
Example - given a relationship
User.addresses -> Address
, specify subquery loading for theUser.addresses
collection, but on eachAddress
object load only theemail_address
attribute:session.query(User).options( subqueryload(User.addresses).load_only(Address.email_address) )
For a statement that has multiple entities, the lead entity can be specifically referred to using the
Load
constructor:stmt = ( select(User, Address) .join(User.addresses) .options( Load(User).load_only(User.name, User.fullname), Load(Address).load_only(Address.email_address), ) )
When used together with the populate_existing execution option only the attributes listed will be refreshed.
- Parameters:
*attrs¶ – Attributes to be loaded, all others will be deferred.
raiseload¶ –
raise
InvalidRequestError
rather than lazy loading a value when a deferred attribute is accessed. Used to prevent unwanted SQL from being emitted.New in version 2.0.
See also
Limiting which Columns Load with Column Deferral - in the ORM Querying Guide
- Parameters:
*attrs¶ – Attributes to be loaded, all others will be deferred.
raiseload¶ –
raise
InvalidRequestError
rather than lazy loading a value when a deferred attribute is accessed. Used to prevent unwanted SQL from being emitted.New in version 2.0.
- function sqlalchemy.orm.undefer(key: Literal['*'] | QueryableAttribute[Any], *addl_attrs: Literal['*'] | QueryableAttribute[Any]) _AbstractLoad ¶
Indicate that the given column-oriented attribute should be undeferred, e.g. specified within the SELECT statement of the entity as a whole.
The column being undeferred is typically set up on the mapping as a
deferred()
attribute.This function is part of the
Load
interface and supports both method-chained and standalone operation.Examples:
# 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) )
- Parameters:
key¶ – Attribute to be undeferred.
- function sqlalchemy.orm.undefer_group(name: str) _AbstractLoad ¶
Indicate that columns within the given deferred group name should be undeferred.
The columns being undeferred are set up on the mapping as
deferred()
attributes and include a “group” name.E.g:
session.query(MyClass).options(undefer_group("large_attrs"))
To undefer a group of attributes on a related entity, the path can be spelled out using relationship loader options, such as
defaultload()
:select(MyClass).options( defaultload("someattr").undefer_group("large_attrs") )
See also
Limiting which Columns Load with Column Deferral - in the ORM Querying Guide
- function sqlalchemy.orm.with_expression(key: _AttrType, expression: _ColumnExpressionArgument[Any]) _AbstractLoad ¶
Apply an ad-hoc SQL expression to a “deferred expression” attribute.
This option is used in conjunction with the
query_expression()
mapper-level construct that indicates an attribute which should be the target of an ad-hoc SQL expression.E.g.:
stmt = select(SomeClass).options( with_expression(SomeClass.x_y_expr, SomeClass.x + SomeClass.y) )
New in version 1.2.
See also
Loading Arbitrary SQL Expressions onto Objects - background and usage examples