Relationship Loading Techniques¶
About this Document
このセクションでは、関連するオブジェクトをロードする方法について詳しく説明します。読者は Relationship Configuration と基本的な使い方に精通している必要があります。
ここでのほとんどの例は、 setup for selectes で説明されているような”User/Address”マッピング設定を想定しています。
SQLAlchemyの重要な部分は、問い合わせ時に関連するオブジェクトがどのようにロードされるかを広範囲にわたって制御することです。”関連するオブジェクト”とは、 relationship()
を使用してマッパーに設定されたコレクションまたはスカラーの関連付けを指します。この動作は、マッパーの構築時に relationship()
関数の relationship.lazy
パラメータを使用して、また Select
構文で**ORM loader options**を使用して設定できます。
関係のロードは、 遅延* *ロード、 **即時 ロード、および ロードなし ロードの3つのカテゴリに分類されます。遅延ロードとは、関連するオブジェクトが最初にロードされずにクエリーから戻されるオブジェクトを指します。特定のオブジェクトで特定のコレクションまたは参照が最初にアクセスされると、要求されたコレクションがロードされるように、追加のSELECT文が発行されます。
Eager Loadingは、関連するコレクションまたはスカラー参照がすでにロードされているクエリから返されたオブジェクトを参照します。ORMは、通常JOINで発行するSELECT文を拡張して関連する行を同時にロードするか、プライマリSELECT文の後に追加のSELECT文を発行してコレクションまたはスカラー参照を一度にロードすることで、これを実現します。
“ロードしない”とは、不要な遅延ロードを防ぐために、指定された関係のロードを無効にすることを指します。つまり、属性が空でロードされないか、アクセス時にエラーが発生します。
Summary of Relationship Loading Styles¶
The primary forms of relationship loading are:
関係ロードの主な形式は次のとおりです。
遅延読み込み -
lazy='select'
またはlazyload()
オプションで使用できます。これは、属性のアクセス時にSELECT文を発行して、関連する参照を一度に1つのオブジェクトに遅延読み込みする形式の読み込みです。遅延読み込みは、relationship.lazy
オプションを示さないすべてのrelationship()
構文の**デフォルトの読み込みスタイル**です。遅延読み込みの詳細は Lazy Loading を参照してください。
select IN loading -
lazy='selectin'
またはselectinload()
オプションで使用できます。この形式の読み込みは、親オブジェクトの主キー識別子をIN句にアセンブルする2番目(またはそれ以上)のSELECT文を発行します。これにより、関連するコレクション/スカラー参照のすべてのメンバーが主キーによって一度に読み込まれます。Select IN loadingの詳細については、 Select IN loading を参照してください。
joined loading -
lazy='joined'
またはjoinedload()
オプションで利用できます。この形式の読み込みは与えられたSELECT文にJOINを適用し、関連する行が同じ結果セットに読み込まれるようにします。joined eager loadingの詳細は Joined Eager Loading を参照してください。
raise loading -
lazy='raise'
、lazy='raise_on_sql'
、またはraiseload()
オプションで使用できます。この形式のロードは、アプリケーションが不要な遅延ロードを行うのを防ぐためにORM例外を発生させる場合を除いて、通常遅延ロードが発生するのと同時にトリガされます。raise loadingの紹介は Preventing unwanted lazy loads using raiseload にあります。
副問い合わせの読み込み -
lazy='subquery'
またはsubqueryload()
オプションで利用できます。この形式の読み込みは、副問い合わせ内に埋め込まれた元の問い合わせを再記述する2番目のSELECT文を発行し、その副問い合わせを関連するテーブルにJOINして、関連するコレクション/スカラ参照のすべてのメンバを一度に読み込みます。副問い合わせのeager loadingについては Subquery Eager Loading を参照してください。
write only loading -
lazy='write_only'
経由、またはWriteOnlyMapped
アノテーションを使用してRelationship
オブジェクトの左側にアノテーションを付けることで利用できます。このコレクションのみのローダースタイルは、データベースからレコードを暗黙的にロードせず、代わりにWriteOnlyCollection.add()
、WriteOnlyCollection.add_all()
およびWriteOnlyCollection.remove()
メソッドのみを許可する代替属性インストルメンテーションを生成します。コレクションのクエリは、WriteOnlyCollection.select()
メソッドを使用して構築されたSELECT文を呼び出すことによって実行されます。書き込みのみのロードについては Write Only Relationships で説明されています。
ダイナミックローディング -
lazy='dynamic'
経由、またはDynamicMapped
アノテーションを使ってRelationship
オブジェクトの左側にアノテーションを付けることで利用できます。これは、コレクションがアクセスされたときにQuery
オブジェクトを生成し、コレクションの内容に対してカスタムSQLを発行できるようにする、従来のコレクションのみのローダースタイルです。しかし、ダイナミックローダーはさまざまな状況で基礎となるコレクションを暗黙的に反復するため、真に大きなコレクションを管理する上であまり有用ではありません。ダイナミックローダーは “write only” コレクションに取って代わられ、いかなる状況においても基礎となるコレクションが暗黙的にロードされるのを防ぎます。ダイナミックローダーについては Dynamic Relationship Loaders で説明されています。
Configuring Loader Strategies at Mapping Time¶
特定の関係のためのローダー戦略は、マッピング時に、マップされた型のオブジェクトがロードされるすべての場合に、それを変更するクエリレベルのオプションがない場合に実行されるように設定できます。これは relationship()
の relationship.lazy
パラメータを使用して設定されます。このパラメータの一般的な値には、 select
、 selectin
、 joined
などがあります。
以下の例は、 One To Many での関係の例を示しています。この例では、 Parent
オブジェクトのSELECT文が発行されたときに、 Parent.children
関係が Select IN loading を使用するように設定しています。:
from typing import List
from sqlalchemy import ForeignKey
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
class Base(DeclarativeBase):
pass
class Parent(Base):
__tablename__ = "parent"
id: Mapped[int] = mapped_column(primary_key=True)
children: Mapped[List["Child"]] = relationship(lazy="selectin")
class Child(Base):
__tablename__ = "child"
id: Mapped[int] = mapped_column(primary_key=True)
parent_id: Mapped[int] = mapped_column(ForeignKey("parent.id"))
上の例では、 Parent
オブジェクトのコレクションがロードされると、それぞれの Parent
オブジェクトにも children
コレクションが追加されます。これは、2番目のクエリを発行する selectin
ローダー戦略を使用します。
relationship.lazy
引数のデフォルト値は Lazy Loading を意味する select
です。
Relationship Loading with Loader Options¶
ロード方法を設定するもう1つの、おそらくより一般的な方法は、 Select.options()
メソッドを使用して、特定の属性に対してクエリごとにロード方法を設定することです。ローダオプションを使用すると、関係のロードを非常に詳細に制御できます。最も一般的なものは joinedload()
、 selectinload()
および lazyload()
です。このオプションは、対象となる特定のクラス/属性を参照するクラスバウンド属性を受け入れます:
from sqlalchemy import select
from sqlalchemy.orm import lazyload
# set children to load lazily
stmt = select(Parent).options(lazyload(Parent.children))
from sqlalchemy.orm import joinedload
# set children to load eagerly with a join
stmt = select(Parent).options(joinedload(Parent.children))
メソッドチェーニング を 使用してローダオプションを”チェーニング”し、さらに深いレベルでのロード方法を指定することもできます。:
from sqlalchemy import select
from sqlalchemy.orm import joinedload
stmt = select(Parent).options(
joinedload(Parent.children).subqueryload(Child.subelements)
)
チェーンローダオプションは、”遅延”ロードされたコレクションに対して適用できます。これは、コレクションまたはアソシエーションがアクセス時に遅延ロードされると、指定されたオプションが有効になることを意味します:
from sqlalchemy import select
from sqlalchemy.orm import lazyload
stmt = select(Parent).options(lazyload(Parent.children).subqueryload(Child.subelements))
上の例では、クエリは children
コレクションがロードされていない Parent
オブジェクトを返します。特定の Parent
オブジェクトの children
コレクションが最初にアクセスされると、関連するオブジェクトが遅延ロードされますが、さらに children
の各メンバーの subelements
コレクションにも強制ロードが適用されます。
Adding Criteria to loader options¶
ローダー・オプションを示すために使用される関係属性には、ローダーの方針に応じて、作成される結合のON句または関連するWHERE条件に追加のフィルタリング条件を追加する機能が含まれます。これは、 PropComparator.and_()
メソッドを使用して実現できます。このメソッドは、ロードされる結果が指定されたフィルタ条件に制限されるようにオプションを通過します:
from sqlalchemy import select
from sqlalchemy.orm import lazyload
stmt = select(A).options(lazyload(A.bs.and_(B.id > 5)))
制限条件を使用する場合、特定のコレクションがすでにロードされている場合、そのコレクションは更新されません。新しい条件が確実に実行されるようにするには、 Populate Existing 実行オプションを適用します。:
from sqlalchemy import select
from sqlalchemy.orm import lazyload
stmt = (
select(A)
.options(lazyload(A.bs.and_(B.id > 5)))
.execution_options(populate_existing=True)
)
ローダの戦略やロードプロセスのどこで発生するかに関係なく、クエリ全体でエンティティのすべての出現にフィルタリング基準を追加するには、 with_loader_criteria()
関数を参照してください。
New in version 1.4.
Specifying Sub-Options with Load.options()¶
メソッドチェイニングを使用すると、パス内の各リンクのローダースタイルが明示的に記述されます。特定の属性の既存のローダースタイルを変更せずにパスに沿ってナビゲートするには、 defaultload()
メソッド/関数を使用できます:
from sqlalchemy import select
from sqlalchemy.orm import defaultload
stmt = select(A).options(defaultload(A.atob).joinedload(B.btoc))
Load.options()
メソッドを使用して、複数のサブオプションを一度に指定するために同様のアプローチを使用できます:
from sqlalchemy import select
from sqlalchemy.orm import defaultload
from sqlalchemy.orm import joinedload
stmt = select(A).options(
defaultload(A.atob).options(joinedload(B.btoc), joinedload(B.btod))
)
See also
Using load_only() on related objects and collections - 関係と列指向のローダオプションを組み合わせた例を示します。
Lazy Loading¶
デフォルトでは、すべてのオブジェクト間の関係は**遅延読み込み**です。 relationship()
に関連付けられたスカラーまたはコレクション属性には、属性が最初にアクセスされたときに起動するトリガが含まれています。このトリガは通常、関連するオブジェクトを読み込むために、アクセスポイントでSQL呼び出しを発行します。:
>>> spongebob.addresses
SELECT
addresses.id AS addresses_id,
addresses.email_address AS addresses_email_address,
addresses.user_id AS addresses_user_id
FROM addresses
WHERE ? = addresses.user_id
[5]
[<Address(u'spongebob@google.com')>, <Address(u'j25@yahoo.com')>]
SQLが発行されないケースの1つは、単純な多対1の関係で、関連するオブジェクトがその主キーのみで識別でき、そのオブジェクトが現在の Session
に既に存在している場合です。このため、遅延読み込みは関連するコレクションにはコストがかかりますが、比較的小さなターゲットオブジェクトのセットに対して単純な多対1で多くのオブジェクトをロードする場合、遅延読み込みは親オブジェクトほど多くのSELECT文を発行せずに、これらのオブジェクトをローカルに参照できます。
“load upon attribute access”のこのデフォルトの動作は、”lazy”または”select”ロードと呼ばれます。”select”という名前は、通常、属性が最初にアクセスされたときに”SELECT”文が発行されるためです。
遅延読み込みは、 lazyload()
ローダオプションを使用して、通常は他の方法で設定されている特定の属性に対して有効にすることができます:
from sqlalchemy import select
from sqlalchemy.orm import lazyload
# force lazy loading for an attribute that is set to
# load some other way normally
stmt = select(User).options(lazyload(User.addresses))
Preventing unwanted lazy loads using raiseload¶
lazyload()
戦略は、オブジェクトリレーショナルマッピングで参照される最も一般的な問題の1つである効果を生成します。 N plus one problem は、ロードされたN個のオブジェクトに対して、その遅延ロードされた属性にアクセスすると、N+1個のSELECT文が発行されることを意味します。SQLAlchemyでは、N+1問題に対する通常の緩和策は、非常に有能なEager Loadシステムを利用することです。ただし、Eager Loadでは、ロードされる属性が Select
で事前に指定されている必要があります。遅延ロードが望ましくない場合に、遅延ロードされなかった他の属性にアクセスするコードの問題は、 raiseload()
戦略を使用して対処できます。このローダー戦略は、遅延ロードの動作を、発生する情報エラーに置き換えます:
from sqlalchemy import select
from sqlalchemy.orm import raiseload
stmt = select(User).options(raiseload(User.addresses))
上の例では、上のクエリからロードされた User
オブジェクトには .addresses
コレクションがロードされていません。後でコードがこの属性にアクセスしようとすると、ORM例外が発生します。
raiseload()
は、いわゆる”ワイルドカード”指定子と一緒に使用して、すべての関係がこの戦略を使用すべきであることを示すことができます。たとえば、1つの属性のみをeager loadingとして設定し、残りのすべてをraiseとして設定するには:
from sqlalchemy import select
from sqlalchemy.orm import joinedload
from sqlalchemy.orm import raiseload
stmt = select(Order).options(joinedload(Order.items), raiseload("*"))
上記のワイルドカードは、 items
以外の Order
だけでなく、 Item
オブジェクトのすべての**すべての**関係に適用されます。 Order
オブジェクトだけに対して raiseload()
を設定するには、 Load
でフルパスを指定します:
from sqlalchemy import select
from sqlalchemy.orm import joinedload
from sqlalchemy.orm import Load
stmt = select(Order).options(joinedload(Order.items), Load(Order).raiseload("*"))
逆に、 Item
オブジェクトに対してのみレイズを設定するには:
stmt = select(Order).options(joinedload(Order.items).raiseload("*"))
raiseload()
オプションは関係属性にのみ適用されます。列指向の属性では、 defer()
オプションは同じように動作する defer.raiseload
オプションをサポートしています。
Tip
“raiseload”戦略は、 unit of work フラッシュプロセス内では 適用されません 。つまり、 Session.flush()
プロセスがその作業を完了するためにコレクションをロードする必要がある場合、 raiseload()
ディレクティブをバイパスしながらロードします。
Joined Eager Loading¶
結合されたEager Loadingは、SQLAlchemy ORMに含まれるEager Loadingの最も古いスタイルです。これは、JOIN(デフォルトではLEFT OUTERジョイン)を発行されたSELECT文に接続し、親と同じ結果セットからターゲット・スカラー/コレクションを移入することで機能します。
マッピングレベルでは、次のようになります。:
class Address(Base):
# ...
user: Mapped[User] = relationship(lazy="joined")
結合されたイーガーローディングは、特に多対1の参照ではなくコレクションに使用される場合、通常、マッピングのデフォルトのローディングオプションとしてではなく、クエリのオプションとして適用されます。これは、 joinedload()
ローダオプションを使用して実現されます。:
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import joinedload
>>> stmt = select(User).options(joinedload(User.addresses)).filter_by(name="spongebob")
>>> spongebob = session.scalars(stmt).unique().all()
SELECT
addresses_1.id AS addresses_1_id,
addresses_1.email_address AS addresses_1_email_address,
addresses_1.user_id AS addresses_1_user_id,
users.id AS users_id, users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
LEFT OUTER JOIN addresses AS addresses_1
ON users.id = addresses_1.user_id
WHERE users.name = ?
['spongebob']
Tip
1対多または多対多のコレクションを参照して joinedload()
をインクルードする場合、返された結果に Result.unique()
メソッドを適用する必要があります。これは、そうでなければ結合によって乗算される主キーによって、入ってくる行を単一化します。これが存在しない場合、ORMはエラーを発生させます。
最新のSQLAlchemyでは、これは自動的には行われません。なぜなら、この文が通常返す行数よりも少ないORMオブジェクトを返すように、結果セットの動作を変更するからです。したがって、SQLAlchemyは Result.unique()
を明示的に使用し続けるので、返されるオブジェクトが主キーで一意化されているという曖昧さはありません。
デフォルトで発行されるJOINはLEFT OUTER JOINで、関連する行を参照しないリードオブジェクトを許可します。参照する外部キーがNOT NULLである関連するオブジェクトへの多対1参照など、要素を持つことが保証されている属性の場合、内部結合を使用することでクエリをより効率的にすることができます。これは、 relationship.innerjoin
フラグを介してマッピングレベルで使用できます:
class Address(Base):
# ...
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
user: Mapped[User] = relationship(lazy="joined", innerjoin=True)
クエリオプションレベルでは、 joinedload.innerjoin
フラグを使用します。:
from sqlalchemy import select
from sqlalchemy.orm import joinedload
stmt = select(Address).options(joinedload(Address.user, innerjoin=True))
JOINは、OUTER JOINを含むチェーンに適用されると、それ自体を右ネストします。:
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import joinedload
>>> stmt = select(User).options(
... joinedload(User.addresses).joinedload(Address.widgets, innerjoin=True)
... )
>>> results = session.scalars(stmt).unique().all()
SELECT
widgets_1.id AS widgets_1_id,
widgets_1.name AS widgets_1_name,
addresses_1.id AS addresses_1_id,
addresses_1.email_address AS addresses_1_email_address,
addresses_1.user_id AS addresses_1_user_id,
users.id AS users_id, users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
LEFT OUTER JOIN (
addresses AS addresses_1 JOIN widgets AS widgets_1 ON
addresses_1.widget_id = widgets_1.id
) ON users.id = addresses_1.user_id
Tip
SELECTを発行する際にデータベースの行ロック技術を使用している場合、つまり Select.with_for_update()
メソッドがSELECT.FOR UPDATEの発行に使用されている場合、使用中のバックエンドの動作によっては、結合されたテーブルもロックされる可能性があります。この理由から、結合されたEager LoadingをSELECT.FOR UPDATEと同時に使用することは推奨されません。
The Zen of Joined Eager Loading¶
結合されたeager loadingは Select.join()
の使用と多くの類似点があるように見えるため、いつどのように使用すべきかについて混乱を引き起こすことがよくあります。 Select.join()
がクエリの結果を変更するために使用されるのに対して、 joinedload()
はクエリの結果を**変更せず**、代わりに関連するオブジェクトのみが存在できるようにレンダリングされた結合の効果を隠すために多大な努力を払っているという違いを理解することが重要です。
ローダ戦略の背後にある哲学は、任意のロードスキームのセットを特定のクエリに適用することができ、 結果は変化しない ということです。関連するオブジェクトとコレクションを完全にロードするために必要なSQL文の数だけが変化します。特定のクエリは、すべての遅延ロードを使用して開始する場合があります。コンテキスト内で使用した後、特定の属性またはコレクションが常にアクセスされていることが明らかになり、これらのためにローダ戦略を変更する方が効率的であることが明らかになる場合があります。戦略はクエリに他の変更を加えることなく変更でき、結果は同一のままですが、生成されるSQL文は少なくなります。理論的には(そして実際にはほとんど)、 Select
に対して何もできないので、ローダ戦略の変更に基づいて、別のプライマリオブジェクトまたは関連オブジェクトのセットがロードされます。
特に、 joinedload()
が返すエンティティ行に影響を与えないようにするには、クエリに追加する結合の匿名エイリアスを作成して、クエリの他の部分から参照できないようにする必要があります。例えば、以下のクエリでは joinedload()
を使って users
から addresses
へのLEFT OUTER JOINを作成していますが、 Address.email_address
に対して追加された ORDER BY
は有効ではありません。 Address
エンティティはクエリ内で指定されていません。
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import joinedload
>>> stmt = (
... select(User)
... .options(joinedload(User.addresses))
... .filter(User.name == "spongebob")
... .order_by(Address.email_address)
... )
>>> result = session.scalars(stmt).unique().all()
SELECT
addresses_1.id AS addresses_1_id,
addresses_1.email_address AS addresses_1_email_address,
addresses_1.user_id AS addresses_1_user_id,
users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
LEFT OUTER JOIN addresses AS addresses_1
ON users.id = addresses_1.user_id
WHERE users.name = ?
ORDER BY addresses.email_address <-- this part is wrong !
['spongebob']
上記では、 addresses
がFROMリストにないので、 ORDER BY addresses.email_address
は有効ではありません。 User
レコードをロードして電子メールアドレスで並べ替える正しい方法は、 Select.join()
を使用することです。
>>> from sqlalchemy import select
>>> stmt = (
... select(User)
... .join(User.addresses)
... .filter(User.name == "spongebob")
... .order_by(Address.email_address)
... )
>>> result = session.scalars(stmt).unique().all()
SELECT
users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
JOIN addresses ON users.id = addresses.user_id
WHERE users.name = ?
ORDER BY addresses.email_address
['spongebob']
上の文はもちろん前の文と同じではありません。つまり、 addresses
からの列は結果にまったく含まれません。 joinedload()
を追加して、2つの結合ができるようにします。1つは順序付けを行う結合で、もう1つは User.addresses
コレクションの内容を読み込むために匿名で使用されます。
>>> stmt = (
... select(User)
... .join(User.addresses)
... .options(joinedload(User.addresses))
... .filter(User.name == "spongebob")
... .order_by(Address.email_address)
... )
>>> result = session.scalars(stmt).unique().all()
SELECT
addresses_1.id AS addresses_1_id,
addresses_1.email_address AS addresses_1_email_address,
addresses_1.user_id AS addresses_1_user_id,
users.id AS users_id, users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users JOIN addresses
ON users.id = addresses.user_id
LEFT OUTER JOIN addresses AS addresses_1
ON users.id = addresses_1.user_id
WHERE users.name = ?
ORDER BY addresses.email_address
['spongebob']
上で見たように、 Select.join()
の使用法は、後続のクエリ条件で使用したいJOIN句を提供することですが、 joinedload()
の使用法は、結果の各 User
に対して User.addresses
コレクションをロードすることだけに関係しています。この場合、2つの結合はおそらく冗長に見えます。つまり、冗長に見えます。コレクションのロードと順序付けに1つのJOINだけを使用したい場合は、以下の Routing Explicit Joins/Statements into Eagerly Loaded Collections で説明されている contains_eager()
オプションを使用します。しかし、 joinedload()
がなぜそのように動作するのかを理解するために、特定の Address
に対して**フィルタリング**している場合を考えてみてください。
>>> stmt = (
... select(User)
... .join(User.addresses)
... .options(joinedload(User.addresses))
... .filter(User.name == "spongebob")
... .filter(Address.email_address == "someaddress@foo.com")
... )
>>> result = session.scalars(stmt).unique().all()
SELECT
addresses_1.id AS addresses_1_id,
addresses_1.email_address AS addresses_1_email_address,
addresses_1.user_id AS addresses_1_user_id,
users.id AS users_id, users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users JOIN addresses
ON users.id = addresses.user_id
LEFT OUTER JOIN addresses AS addresses_1
ON users.id = addresses_1.user_id
WHERE users.name = ? AND addresses.email_address = ?
['spongebob', 'someaddress@foo.com']
上の図から、2つのJOINの役割が非常に異なることがわかります。1つはちょうど1つの行、つまり Address.email_address=='someaddress@foo.com'
の場合の User
と Address
の結合の行に一致します。もう1つのLEFT OUTER JOINは、 User
に関連する すべての
Address
行に一致し、返された User
オブジェクトの User.addresses
コレクションを作成するためだけに使用されます。
joinedload()
の使用方法を別のロード方法に変更することで、実際に必要な User
行を取得するために使用されるSQLとは完全に独立して、コレクションのロード方法を変更することができます。以下では、 joinedload()
を selectinload()
に変更します。
>>> stmt = (
... select(User)
... .join(User.addresses)
... .options(selectinload(User.addresses))
... .filter(User.name == "spongebob")
... .filter(Address.email_address == "someaddress@foo.com")
... )
>>> result = session.scalars(stmt).all()
SELECT
users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
JOIN addresses ON users.id = addresses.user_id
WHERE
users.name = ?
AND addresses.email_address = ?
['spongebob', 'someaddress@foo.com']
# ... selectinload() emits a SELECT in order
# to load all address records ...
結合されたEager Loadingを使用する場合、DISTINCT、LIMIT、OFFSET、または同等のものを使用する場合など、結合に対して外部から返されるローに影響を与える修飾子がクエリに含まれていると、完成した文は最初にサブクエリ内にラップされ、結合されたEager Loading専用に使用される結合がサブクエリに適用されます。SQLAlchemyの結合されたEager Loadingは、クエリの形式に関係なく、コレクションと関連オブジェクトのロード方法だけに影響を与え、クエリの最終結果には影響を与えないようにするために、さらに1マイル、さらに10マイル先まで進んでいます。
Select IN loading¶
ほとんどの場合、オブジェクトのコレクションを積極的に読み込むには、セレクチンの読み込みが最も簡単で効率的な方法です。セレクチンの積極的な読み込みが実行できない唯一のシナリオは、モデルが複合プライマリ・キーを使用しており、バックエンド・データベースがINを持つタプルをサポートしていない場合です。これには現在SQL Serverが含まれています。
“Select IN”eager loadingは、 relationship.lazy
の引数 "selectin"
またはローダオプションの selectinload()
を使用して提供されます。このスタイルの読み込みは、親オブジェクトのプライマリキー値を参照するSELECTを発行します。子オブジェクトのプライマリキー値に対する多対1の関係の場合は、IN句の内部で、関連する関連付けを読み込むために次のようなSELECTを発行します。
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import selectinload
>>> stmt = (
... select(User)
... .options(selectinload(User.addresses))
... .filter(or_(User.name == "spongebob", User.name == "ed"))
... )
>>> result = session.scalars(stmt).all()
SELECT
users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
WHERE users.name = ? OR users.name = ?
('spongebob', 'ed')
SELECT
addresses.id AS addresses_id,
addresses.email_address AS addresses_email_address,
addresses.user_id AS addresses_user_id
FROM addresses
WHERE addresses.user_id IN (?, ?)
(5, 7)
上の例では、2番目のSELECTは addresses.user_id IN(5, 7)
を参照しています。ここで、 5
と 7
は、ロードされた前の2つの User
オブジェクトの主キー値です。オブジェクトのバッチが完全にロードされた後、それらの主キー値は2番目のSELECTの IN
句に注入されます。 User
と Address
の関係は単純なプライマリジョイン条件を持ち、 User
の主キー値が Address.user_id
から得られることを規定しているので、この文にはジョインもサブクエリもありません。
単純な多対1のロードでは、親オブジェクトからの外部キー値が使用されるため、JOINも必要ありません。
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import selectinload
>>> stmt = select(Address).options(selectinload(Address.user))
>>> result = session.scalars(stmt).all()
SELECT
addresses.id AS addresses_id,
addresses.email_address AS addresses_email_address,
addresses.user_id AS addresses_user_id
FROM addresses
SELECT
users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
WHERE users.id IN (?, ?)
(1, 2)
Tip
“単純”とは、 relationship.primaryjoin
条件が、追加の条件なしで、”1”側の主キーと”多”側の直接の外部キーとの間の等価比較を表すことを意味します。
SE LE CT INロードでは、多対多の関係もサポートされています。この場合、現在は3つのテーブルすべてにわたってJOINが行われ、一方の側から他方の側へのローが照合されます。
この種のロードについて知っておくべきことは次のとおりです。:
この方法では、プライマリ・キーがSQL文の大きなIN式にレンダリングされるため、一度に最大500個の親プライマリ・キー値に対してSELECTが発行されます。Oracleなどの一部のデータベースでは、IN式の大きさに厳しい制限があり、SQL文字列の全体的なサイズは任意に大きくすべきではありません。
“selectin”ロードはINに依存するので、複合主キーを持つマッピングでは、”tuple”形式のINを使用する必要があります。これは
WHERE (table.column_a, table.column_b) IN ((?, ?), (?, ?), (?, ?))
.のようになります。この構文は現在SQL Serverではサポートされておらず、SQLiteでは少なくともバージョン3.15が必要です。SQLAlchemyには、どのプラットフォームがこの構文をサポートしているかを事前にチェックするための特別なロジックはありません。サポートしていないプラットフォームに対して実行すると、データベースはすぐにエラーを返します。失敗するためにSQLを実行するだけのSQLAlchemyの利点は、特定のデータベースがこの構文をサポートし始めた場合、SQLAlchemyを変更しなくても動作することです(SQLiteの場合と同様)。
Subquery Eager Loading¶
Legacy Feature
substanceload()
eager loaderはこの時点ではほとんどがレガシーで、 selectinload()
戦略に取って代わられました。この戦略はずっとシンプルな設計で、 Yield Per のような機能でより柔軟で、ほとんどの場合、より効率的なSQL文を生成します。 substanceload()
は元のSELECT文の再解釈に依存しているので、非常に複雑なソースクエリが与えられた場合、効率的に動作しない可能性があります。
substringload()
は、”tuple IN”構文をサポートしていないMicrosoft SQL Serverのバックエンドで、複合プライマリキーを使用するオブジェクトのコレクションを積極的にロードする場合に便利です。
副問い合わせの読み込みは、selectin eager loadingと動作は似ていますが、生成されるSELECT文は元の文から派生したもので、selectin eager loadingよりも複雑な問い合わせ構造を持っています。
副問い合わせのeager loadingは、 relationship.lazy
の引数 "subquery"
か、 subqueryload()
ローダオプションを使って提供されます。
副問合せのEager Loading操作では、ロードされる関係ごとに、すべての結果オブジェクトに対して2番目のSELECT文が一度に発行されます。このSELECT文は、副問合せ内にラップされた元のSELECT文を参照するため、戻されるプライマリ・オブジェクトに対して同じプライマリ・キーのリストを取得し、それをすべてのコレクション・メンバーの合計にリンクして一度にロードします。
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import subqueryload
>>> stmt = select(User).options(subqueryload(User.addresses)).filter_by(name="spongebob")
>>> results = session.scalars(stmt).all()
SELECT
users.id AS users_id,
users.name AS users_name,
users.fullname AS users_fullname,
users.nickname AS users_nickname
FROM users
WHERE users.name = ?
('spongebob',)
SELECT
addresses.id AS addresses_id,
addresses.email_address AS addresses_email_address,
addresses.user_id AS addresses_user_id,
anon_1.users_id AS anon_1_users_id
FROM (
SELECT users.id AS users_id
FROM users
WHERE users.name = ?) AS anon_1
JOIN addresses ON anon_1.users_id = addresses.user_id
ORDER BY anon_1.users_id, addresses.id
('spongebob',)
この種のロードについて知っておくべきことは次のとおりです。:
“subquery”ローダー・ストラテジーによって発行されるSELECTステートメントは、”selectin”ローダー・ストラテジーとは異なり、サブクエリを必要とし、元のクエリに存在するすべてのパフォーマンス制限を継承します。サブクエリ自体も、使用中のデータベースの特性に基づいてパフォーマンス上のペナルティを受ける場合があります。
“subquery”読み込みは、正しく動作するためにいくつかの特別な順序付け要件を課します。
subqueryload()
をSelect.limit()
やSelect.offset()
のような制限修飾子と組み合わせて使用する問い合わせは、プライマリキーのような一意の列に対してSelect.order_by()
を 常に 含むべきです。これにより、subqueryload()
によって発行される追加の問い合わせは、親の問い合わせで使用されるのと同じ順序付けを含みます。これがないと、内部の問い合わせが間違った行を返す可能性があります:# incorrect, no ORDER BY stmt = select(User).options(subqueryload(User.addresses).limit(1)) # incorrect if User.name is not unique stmt = select(User).options(subqueryload(User.addresses)).order_by(User.name).limit(1) # correct stmt = ( select(User) .options(subqueryload(User.addresses)) .order_by(User.name, User.id) .limit(1) )
See also
Why is ORDER BY recommended with LIMIT (especially with subqueryload())? - detailed example
“subquery”のロードは、サブクエリが繰り返しネストされるため、多くのレベルの深いEager Loadで使用すると、パフォーマンスや複雑さの問題も発生します。
“subquery”のロードは、 Yield Per が提供する”batched”ロードとは、コレクションとスカラーの両方の関係において互換性がありません。
上記の理由から、”selectin”戦略は”subquery”よりも優先されるべきです。
See also
What Kind of Loading to Use ?¶
通常、どのタイプのロードを使用するかは、SQLの実行回数、生成されるSQLの複雑さ、フェッチされるデータの量の間のトレードオフを最適化することになります。
One to Many/Many to Manyコレクション - selectinload()
は、一般的に使用するのに最適なロード方法です。これは追加のSELECTを発行し、元の文に影響を与えずにできるだけ少ないテーブルを使用し、あらゆる種類の元のクエリに対して最も柔軟です。唯一の大きな制限は、”tuple IN”をサポートしていないバックエンドで複合プライマリキーを持つテーブルを使用する場合です。”tuple IN”には現在SQL Serverと非常に古いバージョンのSQLiteが含まれていますが、他のすべてのバックエンドではサポートされています。
Many to One - joinedload()
戦略は最も汎用的な戦略です。特殊なケースでは、 immediateload()
戦略も、関連する可能性のある値の数が非常に少ない場合に便利です。なぜなら、この戦略は、関連するオブジェクトがすでに存在する場合に、SQLを生成せずにローカルの Session
からオブジェクトを取得するからです。
Polymorphic Eager Loading¶
with_polymorphic()
関数と組み合わせた PropComparator.of_type()
メソッドの例については、 Eager Loading of Polymorphic Subtypes を参照してください。
Wildcard Loading Strategies¶
joinedload()
,:func:.unsignedload,:func:.lazyload,:func:.selectinload ,および raiseload()
のそれぞれを使用して、特定の問い合わせに対して relationship()
のデフォルトのロードスタイルを設定することができます。これは、文の中で他に指定されていない relationship()
にマップされたすべての属性に影響します。この機能は、以下のオプションの引数として文字列 '*'
を渡すことで利用できます:
from sqlalchemy import select
from sqlalchemy.orm import lazyload
stmt = select(MyClass).options(lazyload("*"))
上記では、 lazyload('*')
オプションは、その問い合わせで使用されているすべての relationship()
構文の lazy
設定よりも優先されます。ただし、 lazy='write_only'
または lazy='dynamic'
を使用するものは例外です。
例えば、いくつかの関係で lazy='joined'
や lazy='selectin'
が指定されている場合、 lazyload('*')
を使用すると、一方的にすべての関係で select'
ロードが使用されます。たとえば、各属性がアクセスされるとSELECT文が出力されます。
このオプションは、 joinedload()
、 selectinload()
などのクエリで指定されたローダオプションよりも優先されるわけではありません。以下のクエリでも、 widget
の関係に結合されたロードを使用します。:
from sqlalchemy import select
from sqlalchemy.orm import lazyload
from sqlalchemy.orm import joinedload
stmt = select(MyClass).options(lazyload("*"), joinedload(MyClass.widget))
上記の joinedload()
の命令は、 lazyload()
オプションの前か後かに関わらず実行されますが、複数のオプションが渡され、それぞれに "*"
が含まれている場合は、最後のオプションが有効になります。
Per-Entity Wildcard Loading Strategies¶
ワイルドカード・ローダー戦略の変形として、エンティティごとに戦略を設定する機能があります。例えば、 User
と Address
を照会する場合、 User
のローダー戦略には影響を与えずに、 Address
のすべての関係に遅延読み込みを使用するように指示できます。そのためには、まず Load
オブジェクトを適用し、次にチェーンオプションとして *
を指定します。:
from sqlalchemy import select
from sqlalchemy.orm import Load
stmt = select(User, Address).options(Load(Address).lazyload("*"))
上記では、 Address
のすべての関係が遅延読み込みに設定されます。
Routing Explicit Joins/Statements into Eagerly Loaded Collections¶
joinedload()
の動作では、匿名の別名をターゲットとして使用して結合が自動的に作成され、その結果がロードされたオブジェクトのコレクションとスカラー参照にルーティングされます。多くの場合、クエリには特定のコレクションまたはスカラー参照を表す必要な結合が既に含まれており、joinedload機能によって追加された結合は冗長ですが、コレクション/参照を読み込む必要があります。
このSQLAlchemyは contains_eager()
オプションを提供します。このオプションは joinedload()
オプションと同じ方法で使用されますが、 Select
オブジェクトが適切な結合を明示的に含むことを想定しており、通常は Select.join()
のようなメソッドを使用します。以下では、 User
と Address
の間の結合を指定し、さらにこれを User.addresses
のeagerロードの基礎として確立します。:
from sqlalchemy.orm import contains_eager
stmt = select(User).join(User.addresses).options(contains_eager(User.addresses))
文の”eager”部分が”aliased”の場合、パスは PropComparator.of_type()
を使って指定する必要があります。これにより、特定の aliased()
構文を渡すことができます。:
# use an alias of the Address entity
adalias = aliased(Address)
# construct a statement which expects the "addresses" results
stmt = (
select(User)
.outerjoin(User.addresses.of_type(adalias))
.options(contains_eager(User.addresses.of_type(adalias)))
)
# get results normally
r = session.scalars(stmt).unique().all()
SELECT
users.user_id AS users_user_id,
users.user_name AS users_user_name,
adalias.address_id AS adalias_address_id,
adalias.user_id AS adalias_user_id,
adalias.email_address AS adalias_email_address,
(...other columns...)
FROM users
LEFT OUTER JOIN email_addresses AS email_addresses_1
ON users.user_id = email_addresses_1.user_id
contains_eager()
への引数として与えられるパスは、開始エンティティからのフルパスでなければなりません。例えば、 Users->orders->Order->items->Item
をロードする場合、このオプションは次のように使用されます。:
stmt = select(User).options(contains_eager(User.orders).contains_eager(Order.items))
Using contains_eager() to load a custom-filtered collection result¶
contains_eager()
を使用すると、コレクションの生成に使用されるSQLが構築されます。このことから、当然のことながら、コレクションまたはスカラー属性の要素のサブセットをロードするSQLを記述することによって、コレクションが格納する値を 変更 することができます。
Tip
PropComparator.and_()
を使用して、 joinedload()
や selectinload()
などのローダオプションにWHERE条件を直接追加できるようにしました。例については Adding Criteria to loader options の節を参照してください。
ここで説明するテクニックは、単純なWHERE句よりも複雑なSQL基準または修飾子を使用して関連するコレクションを照会する場合にも適用されます。
一例として、 User
オブジェクトをロードし、特定のアドレスだけをその .addresses
コレクションに積極的にロードすることができます。そのためには、結合されたデータをフィルタリングし、 contains_eager()
を使ってルーティングします。また、 Populate Existing を使って、すでにロードされているコレクションが上書きされるようにします。:
stmt = (
select(User)
.join(User.addresses)
.filter(Address.email_address.like("%@aol.com"))
.options(contains_eager(User.addresses))
.execution_options(populate_existing=True)
)
上のクエリは、少なくとも email
フィールドに aol.com
という部分文字列を含む Address
オブジェクトを含む User
オブジェクトのみをロードします。 User.addresses
コレクションには、これらの Address
エントリのみが含まれ、実際にコレクションに関連付けられている他の Address
エントリは含まれません。
..tip:: いずれの場合も、SQLAlchemy ORMは すでにロードされた属性とコレクションを上書きしません そうするように指示されない限り。 identity map が使用されているため、ORMクエリが実際にはすでに存在し、メモリにロードされているオブジェクトを返すことがよくあります。したがって 、contains_eager()
を使用して別の方法でコレクションを読み込む場合、通常は上記のように Populate Existing を使用して、すでにロードされているコレクションを新しいデータで更新することをお勧めします。populate_existing`オプションは、保留中の変更を含めて、すでに存在していた **すべての** 属性をリセットしますので、使用する前にすべてのデータをフラッシュしてください。 :class:`_orm.Session をデフォルトの動作である autoflush で使用すれば十分です。
Note
contains_eager()
を使用してロードするカスタマイズされたコレクションは”sticky”ではありません。つまり、このコレクションが次にロードされるときには、通常のデフォルトの内容でロードされます。オブジェクトが期限切れの場合、コレクションは再ロードされます。期限切れは、デフォルトのセッション設定を想定して Session.commit()
、 Session.rollback()
メソッドが使用されるとき、または Session.expire_all()
または Session.expire()
メソッドが使用されるときに発生します。
See also
Adding Criteria to loader options - 任意の関係ローダオプション内で直接WHERE条件を許可する最新のAPI
Relationship Loader API¶
Object Name | Description |
---|---|
contains_eager(*keys, **kw) |
Indicate that the given attribute should be eagerly loaded from columns stated manually in the query. |
defaultload(*keys) |
Indicate an attribute should load using its predefined loader style. |
immediateload(*keys, [recursion_depth]) |
Indicate that the given attribute should be loaded using an immediate load with a per-attribute SELECT statement. |
joinedload(*keys, **kw) |
Indicate that the given attribute should be loaded using joined eager loading. |
lazyload(*keys) |
Indicate that the given attribute should be loaded using “lazy” loading. |
Represents loader options which modify the state of a
ORM-enabled |
|
noload(*keys) |
Indicate that the given relationship attribute should remain unloaded. |
raiseload(*keys, **kw) |
Indicate that the given attribute should raise an error if accessed. |
selectinload(*keys, [recursion_depth]) |
Indicate that the given attribute should be loaded using SELECT IN eager loading. |
subqueryload(*keys) |
Indicate that the given attribute should be loaded using subquery eager loading. |
- function sqlalchemy.orm.contains_eager(*keys: Literal['*'] | QueryableAttribute[Any], **kw: Any) _AbstractLoad ¶
Indicate that the given attribute should be eagerly loaded from columns stated manually in the query.
This function is part of the
Load
interface and supports both method-chained and standalone operation.The option is used in conjunction with an explicit join that loads the desired rows, i.e.:
sess.query(Order).join(Order.user).options( contains_eager(Order.user) )
The above query would join from the
Order
entity to its relatedUser
entity, and the returnedOrder
objects would have theOrder.user
attribute pre-populated.It may also be used for customizing the entries in an eagerly loaded collection; queries will normally want to use the Populate Existing execution option assuming the primary collection of parent objects may already have been loaded:
sess.query(User).join(User.addresses).filter( Address.email_address.like("%@aol.com") ).options(contains_eager(User.addresses)).populate_existing()
See the section Routing Explicit Joins/Statements into Eagerly Loaded Collections for complete usage details.
- function sqlalchemy.orm.defaultload(*keys: Literal['*'] | QueryableAttribute[Any]) _AbstractLoad ¶
Indicate an attribute should load using its predefined loader style.
The behavior of this loading option is to not change the current loading style of the attribute, meaning that the previously configured one is used or, if no previous style was selected, the default loading will be used.
This method is used to link to other loader options further into a chain of attributes without altering the loader style of the links along the chain. For example, to set joined eager loading for an element of an element:
session.query(MyClass).options( defaultload(MyClass.someattribute).joinedload( MyOtherClass.someotherattribute ) )
defaultload()
is also useful for setting column-level options on a related class, namely that ofdefer()
andundefer()
:session.scalars( select(MyClass).options( defaultload(MyClass.someattribute) .defer("some_column") .undefer("some_other_column") ) )
- function sqlalchemy.orm.immediateload(*keys: Literal['*'] | QueryableAttribute[Any], recursion_depth: int | None = None) _AbstractLoad ¶
Indicate that the given attribute should be loaded using an immediate load with a per-attribute SELECT statement.
The load is achieved using the “lazyloader” strategy and does not fire off any additional eager loaders.
The
immediateload()
option is superseded in general by theselectinload()
option, which performs the same task more efficiently by emitting a SELECT for all loaded objects.This function is part of the
Load
interface and supports both method-chained and standalone operation.- Parameters:
recursion_depth¶ –
optional int; when set to a positive integer in conjunction with a self-referential relationship, indicates “selectin” loading will continue that many levels deep automatically until no items are found.
Note
The
immediateload.recursion_depth
option currently supports only self-referential relationships. There is not yet an option to automatically traverse recursive structures with more than one relationship involved.Warning
This parameter is new and experimental and should be treated as “alpha” status
New in version 2.0: added
immediateload.recursion_depth
- function sqlalchemy.orm.joinedload(*keys: Literal['*'] | QueryableAttribute[Any], **kw: Any) _AbstractLoad ¶
Indicate that the given attribute should be loaded using joined eager loading.
This function is part of the
Load
interface and supports both method-chained and standalone operation.examples:
# joined-load the "orders" collection on "User" select(User).options(joinedload(User.orders)) # joined-load Order.items and then Item.keywords select(Order).options( joinedload(Order.items).joinedload(Item.keywords) ) # lazily load Order.items, but when Items are loaded, # joined-load the keywords collection select(Order).options( lazyload(Order.items).joinedload(Item.keywords) )
- Parameters:
innerjoin¶ –
if
True
, indicates that the joined eager load should use an inner join instead of the default of left outer join:select(Order).options(joinedload(Order.user, innerjoin=True))
In order to chain multiple eager joins together where some may be OUTER and others INNER, right-nested joins are used to link them:
select(A).options( joinedload(A.bs, innerjoin=False).joinedload( B.cs, innerjoin=True ) )
The above query, linking A.bs via “outer” join and B.cs via “inner” join would render the joins as “a LEFT OUTER JOIN (b JOIN c)”. When using older versions of SQLite (< 3.7.16), this form of JOIN is translated to use full subqueries as this syntax is otherwise not directly supported.
The
innerjoin
flag can also be stated with the term"unnested"
. This indicates that an INNER JOIN should be used, unless the join is linked to a LEFT OUTER JOIN to the left, in which case it will render as LEFT OUTER JOIN. For example, supposingA.bs
is an outerjoin:select(A).options( joinedload(A.bs).joinedload(B.cs, innerjoin="unnested") )
The above join will render as “a LEFT OUTER JOIN b LEFT OUTER JOIN c”, rather than as “a LEFT OUTER JOIN (b JOIN c)”.
Note
The “unnested” flag does not affect the JOIN rendered from a many-to-many association table, e.g. a table configured as
relationship.secondary
, to the target table; for correctness of results, these joins are always INNER and are therefore right-nested if linked to an OUTER join.Note
The joins produced by
joinedload()
are anonymously aliased. The criteria by which the join proceeds cannot be modified, nor can the ORM-enabledSelect
or legacyQuery
refer to these joins in any way, including ordering. See The Zen of Joined Eager Loading for further detail.To produce a specific SQL JOIN which is explicitly available, use
Select.join()
andQuery.join()
. To combine explicit JOINs with eager loading of collections, usecontains_eager()
; see Routing Explicit Joins/Statements into Eagerly Loaded Collections.
- function sqlalchemy.orm.lazyload(*keys: Literal['*'] | QueryableAttribute[Any]) _AbstractLoad ¶
Indicate that the given attribute should be loaded using “lazy” loading.
This function is part of the
Load
interface and supports both method-chained and standalone operation.
- class sqlalchemy.orm.Load¶
Represents loader options which modify the state of a ORM-enabled
Select
or a legacyQuery
in order to affect how various mapped attributes are loaded.The
Load
object is in most cases used implicitly behind the scenes when one makes use of a query option likejoinedload()
,defer()
, or similar. It typically is not instantiated directly except for in some very specific cases.See also
Per-Entity Wildcard Loading Strategies - illustrates an example where direct use of
Load
may be usefulMembers
contains_eager(), defaultload(), defer(), get_children(), immediateload(), inherit_cache, joinedload(), lazyload(), load_only(), noload(), options(), process_compile_state(), process_compile_state_replaced_entities(), propagate_to_loaders, raiseload(), selectin_polymorphic(), selectinload(), subqueryload(), undefer(), undefer_group(), with_expression()
Class signature
class
sqlalchemy.orm.Load
(sqlalchemy.orm.strategy_options._AbstractLoad
)-
method
sqlalchemy.orm.Load.
contains_eager(attr: _AttrType, alias: _FromClauseArgument | None = None, _is_chain: bool = False, _propagate_to_loaders: bool = False) Self ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.contains_eager
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Produce a new
Load
object with thecontains_eager()
option applied.See
contains_eager()
for usage examples.
-
method
sqlalchemy.orm.Load.
defaultload(attr: Literal['*'] | QueryableAttribute[Any]) Self ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.defaultload
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Produce a new
Load
object with thedefaultload()
option applied.See
defaultload()
for usage examples.
-
method
sqlalchemy.orm.Load.
defer(key: Literal['*'] | QueryableAttribute[Any], raiseload: bool = False) Self ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.defer
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Produce a new
Load
object with thedefer()
option applied.See
defer()
for usage examples.
-
method
sqlalchemy.orm.Load.
get_children(*, omit_attrs: Tuple[str, ...] = (), **kw: Any) Iterable[HasTraverseInternals] ¶ inherited from the
HasTraverseInternals.get_children()
method ofHasTraverseInternals
Return immediate child
HasTraverseInternals
elements of thisHasTraverseInternals
.This is used for visit traversal.
**kw may contain flags that change the collection that is returned, for example to return a subset of items in order to cut down on larger traversals, or to return child items from a different context (such as schema-level collections instead of clause-level).
-
method
sqlalchemy.orm.Load.
immediateload(attr: Literal['*'] | QueryableAttribute[Any], recursion_depth: int | None = None) Self ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.immediateload
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Produce a new
Load
object with theimmediateload()
option applied.See
immediateload()
for usage examples.
-
attribute
sqlalchemy.orm.Load.
inherit_cache: bool | None = None¶ inherited from the
HasCacheKey.inherit_cache
attribute ofHasCacheKey
Indicate if this
HasCacheKey
instance should make use of the cache key generation scheme used by its immediate superclass.The attribute defaults to
None
, which indicates that a construct has not yet taken into account whether or not its appropriate for it to participate in caching; this is functionally equivalent to setting the value toFalse
, except that a warning is also emitted.This flag can be set to
True
on a particular class, if the SQL that corresponds to the object does not change based on attributes which are local to this class, and not its superclass.See also
Enabling Caching Support for Custom Constructs - General guideslines for setting the
HasCacheKey.inherit_cache
attribute for third-party or user defined SQL constructs.
-
method
sqlalchemy.orm.Load.
joinedload(attr: Literal['*'] | QueryableAttribute[Any], innerjoin: bool | None = None) Self ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.joinedload
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Produce a new
Load
object with thejoinedload()
option applied.See
joinedload()
for usage examples.
-
method
sqlalchemy.orm.Load.
lazyload(attr: Literal['*'] | QueryableAttribute[Any]) Self ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.lazyload
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Produce a new
Load
object with thelazyload()
option applied.See
lazyload()
for usage examples.
-
method
sqlalchemy.orm.Load.
load_only(*attrs: Literal['*'] | QueryableAttribute[Any], raiseload: bool = False) Self ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.load_only
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Produce a new
Load
object with theload_only()
option applied.See
load_only()
for usage examples.
-
method
sqlalchemy.orm.Load.
noload(attr: Literal['*'] | QueryableAttribute[Any]) Self ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.noload
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Produce a new
Load
object with thenoload()
option applied.See
noload()
for usage examples.
-
method
sqlalchemy.orm.Load.
options(*opts: _AbstractLoad) Self ¶ Apply a series of options as sub-options to this
Load
object.E.g.:
query = session.query(Author) query = query.options( joinedload(Author.book).options( load_only(Book.summary, Book.excerpt), joinedload(Book.citations).options( joinedload(Citation.author) ) ) )
- Parameters:
*opts¶ – A series of loader option objects (ultimately
Load
objects) which should be applied to the path specified by thisLoad
object.
New in version 1.3.6.
-
method
sqlalchemy.orm.Load.
process_compile_state(compile_state: ORMCompileState) None ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.process_compile_state
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Apply a modification to a given
ORMCompileState
.This method is part of the implementation of a particular
CompileStateOption
and is only invoked internally when an ORM query is compiled.
-
method
sqlalchemy.orm.Load.
process_compile_state_replaced_entities(compile_state: ORMCompileState, mapper_entities: Sequence[_MapperEntity]) None ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.process_compile_state_replaced_entities
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Apply a modification to a given
ORMCompileState
, given entities that were replaced by with_only_columns() or with_entities().This method is part of the implementation of a particular
CompileStateOption
and is only invoked internally when an ORM query is compiled.New in version 1.4.19.
-
attribute
sqlalchemy.orm.Load.
propagate_to_loaders: bool¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.propagate_to_loaders
attribute ofsqlalchemy.orm.strategy_options._AbstractLoad
if True, indicate this option should be carried along to “secondary” SELECT statements that occur for relationship lazy loaders as well as attribute load / refresh operations.
-
method
sqlalchemy.orm.Load.
raiseload(attr: Literal['*'] | QueryableAttribute[Any], sql_only: bool = False) Self ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.raiseload
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Produce a new
Load
object with theraiseload()
option applied.See
raiseload()
for usage examples.
-
method
sqlalchemy.orm.Load.
selectin_polymorphic(classes: Iterable[Type[Any]]) Self ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.selectin_polymorphic
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Produce a new
Load
object with theselectin_polymorphic()
option applied.See
selectin_polymorphic()
for usage examples.
-
method
sqlalchemy.orm.Load.
selectinload(attr: Literal['*'] | QueryableAttribute[Any], recursion_depth: int | None = None) Self ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.selectinload
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Produce a new
Load
object with theselectinload()
option applied.See
selectinload()
for usage examples.
-
method
sqlalchemy.orm.Load.
subqueryload(attr: Literal['*'] | QueryableAttribute[Any]) Self ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.subqueryload
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Produce a new
Load
object with thesubqueryload()
option applied.See
subqueryload()
for usage examples.
-
method
sqlalchemy.orm.Load.
undefer(key: Literal['*'] | QueryableAttribute[Any]) Self ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.undefer
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Produce a new
Load
object with theundefer()
option applied.See
undefer()
for usage examples.
-
method
sqlalchemy.orm.Load.
undefer_group(name: str) Self ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.undefer_group
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Produce a new
Load
object with theundefer_group()
option applied.See
undefer_group()
for usage examples.
-
method
sqlalchemy.orm.Load.
with_expression(key: _AttrType, expression: _ColumnExpressionArgument[Any]) Self ¶ inherited from the
sqlalchemy.orm.strategy_options._AbstractLoad.with_expression
method ofsqlalchemy.orm.strategy_options._AbstractLoad
Produce a new
Load
object with thewith_expression()
option applied.See
with_expression()
for usage examples.
-
method
- function sqlalchemy.orm.noload(*keys: Literal['*'] | QueryableAttribute[Any]) _AbstractLoad ¶
Indicate that the given relationship attribute should remain unloaded.
The relationship attribute will return
None
when accessed without producing any loading effect.This function is part of the
Load
interface and supports both method-chained and standalone operation.noload()
applies torelationship()
attributes only.Legacy Feature
The
noload()
option is legacy. As it forces collections to be empty, which invariably leads to non-intuitive and difficult to predict results. There are no legitimate uses for this option in modern SQLAlchemy.See also
- function sqlalchemy.orm.raiseload(*keys: Literal['*'] | QueryableAttribute[Any], **kw: Any) _AbstractLoad ¶
Indicate that the given attribute should raise an error if accessed.
A relationship attribute configured with
raiseload()
will raise anInvalidRequestError
upon access. The typical way this is useful is when an application is attempting to ensure that all relationship attributes that are accessed in a particular context would have been already loaded via eager loading. Instead of having to read through SQL logs to ensure lazy loads aren’t occurring, this strategy will cause them to raise immediately.raiseload()
applies torelationship()
attributes only. In order to apply raise-on-SQL behavior to a column-based attribute, use thedefer.raiseload
parameter on thedefer()
loader option.- Parameters:
sql_only¶ – if True, raise only if the lazy load would emit SQL, but not if it is only checking the identity map, or determining that the related value should just be None due to missing keys. When False, the strategy will raise for all varieties of relationship loading.
This function is part of the
Load
interface and supports both method-chained and standalone operation.
- function sqlalchemy.orm.selectinload(*keys: Literal['*'] | QueryableAttribute[Any], recursion_depth: int | None = None) _AbstractLoad ¶
Indicate that the given attribute should be loaded using SELECT IN eager loading.
This function is part of the
Load
interface and supports both method-chained and standalone operation.examples:
# selectin-load the "orders" collection on "User" select(User).options(selectinload(User.orders)) # selectin-load Order.items and then Item.keywords select(Order).options( selectinload(Order.items).selectinload(Item.keywords) ) # lazily load Order.items, but when Items are loaded, # selectin-load the keywords collection select(Order).options( lazyload(Order.items).selectinload(Item.keywords) )
- Parameters:
recursion_depth¶ –
optional int; when set to a positive integer in conjunction with a self-referential relationship, indicates “selectin” loading will continue that many levels deep automatically until no items are found.
Note
The
selectinload.recursion_depth
option currently supports only self-referential relationships. There is not yet an option to automatically traverse recursive structures with more than one relationship involved.Additionally, the
selectinload.recursion_depth
parameter is new and experimental and should be treated as “alpha” status for the 2.0 series.New in version 2.0: added
selectinload.recursion_depth
- function sqlalchemy.orm.subqueryload(*keys: Literal['*'] | QueryableAttribute[Any]) _AbstractLoad ¶
Indicate that the given attribute should be loaded using subquery eager loading.
This function is part of the
Load
interface and supports both method-chained and standalone operation.examples:
# subquery-load the "orders" collection on "User" select(User).options(subqueryload(User.orders)) # subquery-load Order.items and then Item.keywords select(Order).options( subqueryload(Order.items).subqueryload(Item.keywords) ) # lazily load Order.items, but when Items are loaded, # subquery-load the keywords collection select(Order).options( lazyload(Order.items).subqueryload(Item.keywords) )