Django クエリセットの defer / only メソッド 直訳

Django 1.2 目前ですが、1.1 からクエリセットのメソッドに、クエリの最適化に利用できる defer()only() メソッドが追加されています。


http://docs.nullpobug.com/django-doc-ja/trunk/ref/models/querysets.html#defer-fields


まあいつものように、自分のために直訳。

defer(*fields)

複雑なデータモデリングが必要な状況では、モデルに大量のフィールドを持っていたり、 フィールドに(テキストフィールドなどで)大量のデータを持っていたり、 Python オブジェクトに変換するのに重い処理が必要だったりするだろう。 クエリセットの結果のうち、必要のないフィールドがわかっている場合、 Django にそれらのフィールドをデータベースから取得しないように指定することができる。


これは defer() にロードしないフィールド名を渡せばいい:

Entry.objects.defer("lede", "body")


この遅延クエリセットもまたモデルインスタンスを返す。 defer で指定された各フィールドは、そのフィールドにアクセスした時にデータベースから取得される。 (一度に全ての遅延フィールドを取得するのではなく、1フィールドだけ)。


defer() の複数呼び出しも可能。 各呼び出しは新しいフィールドを遅延セットに追加する:

# body と lede フィールドの取得を延期する
Entry.objects.defer("body").filter(headline="Lennon").defer("lede")


遅延セットへの追加順は関係ない。 既に遅延指定されたフィールド名の defer() 再呼び出しも問題ない(そのフィールドは遅延指定されたまま)。


(select_related() でロードされていれば)関連モデルのフィールドに対する遅延ロードも、 関連フィールドを標準的な二重アンダースコアの記述する方法で指定可能:

Blog.objects.select_related().defer("entry__lede", "entry__body")


遅延フィールドの解除は defer() にパラメータとして None を渡せばいい:

# 遅延せずに全フィールドを読み込む
my_queryset.defer(None)


いくつかのフィールドは、指定したとしても遅延処理されない。 主キーは決して遅延読み込みはできない。 select_related() を使って同時に他のモデルを取得する場合、 主モデルから接続される関連モデルフィールドの遅延読み込みをするべきではない (その場ではエラーにはならないが、結果としてエラーが発生する)。

note:

defer() メソッド(と兄弟の only() メソッド)は、高度なユースケースのために存在している。これらはクエリ分析時の最適化や、どの情報が必要かを 厳密に 把握している場合や、 モデルにおいて、必要なフィールドだけ読み込んだ場合と全てのフィールドを返した場合で大きな違いがあることが測定されている場合のために提供されている。 アプリケーション開発の初期の段階から、わざわざ defer() を使う必要はない; クエリ設計が落ち着いて、どこが問題点なのか理解できるまで触るべきではない。

only(*fields)

only() メソッドは defer() メソッドとほぼ反対のメソッドだ。 モデルを取得する際に、遅延取得 しない フィールドを指定して呼び出す。 ほとんどのフィールドを遅延読み込みするようなモデルの場合に、コードを簡潔にするために 遅延処理しないフィールドを指定するために only() を使用する。


name, age, biography フィールドを持つモデルの場合、次の2つのクエリセットは 遅延フィールドとして同じものになる:

Person.objects.defer("age", "biography")
Person.objects.only("name")


only() 呼び出しはいつでも、即時ロードするフィールドセットを 置換 する。 このメソッド名は覚えやすい用につけたもので: 指定したフィールド だけ 即時ロードされ; 残りのフィールドは遅延ロードされる。 このため、連続した only() 呼び出しは、最後の指定されたフィールドだけが考慮される:

# これは headline 以外のフィールドが遅延処理される
Entry.objects.only("body", "lede").only("headline")


defer() は追加処理される(遅延処理リストにフィールドを追加していく)ため、 only()defer() の結合呼び出しが可能であり、これらは論理的にふるまう:

# 最終的に "headline" 以外が遅延処理される。
Entry.objects.only("headline", "body").defer("body")

# 最終的に headline と body が即時処理される
# (only() が全フィールドセットを置換するため)
Entry.objects.defer("body").only("headline", "body")