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() オプションを UserBook の両方に適用したければ、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)

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,)

summarycover_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 value None, unless on the mapping the query_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 the A 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 of deferred() construct is provided by using the mapped_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 the name and fullname attributes:

session.query(User).options(load_only(User.name, User.fullname))

Example - given a relationship User.addresses -> Address, specify subquery loading for the User.addresses collection, but on each Address object load only the email_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.

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")
)
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.

Parameters:
  • key – Attribute to be populated

  • expr – SQL expression to be applied to the attribute.

See also

Loading Arbitrary SQL Expressions onto Objects - background and usage examples