Prefetch オブジェクトについて
以下のようなコードがあった場合
parent_qs = Parent.objects.filter(name="hoge").prefetch_related("children_set") for parent in parent_qs: my_children = parent.children_set.all() ...
という基本形であったとする。これはうまく機能する。
ただし、最後の for 文の中で
for parent in parent_qs: my_children = parent.children_set.filter(age__lt=10)
等と filter を使ってしまうと Prefetch がうまく機能しない。
このような場合は Prefetch オブジェクトを使う。
from django.db import Prefetch parent_qs = Parent.objects.filter(name="hoge").prefetch_related( Prefetch( "children_set", queryset=Children.objects.filter(age__lt=10), ) ) for parent in parent_qs: my_children = parent.children_set.all() ...
for 文は all() でよい。これで age__lt=10 のフィルターが効く。
また、prefetch_related の中で select_related を使いたい場合も Prefetch オブジェクトを使う。
parent_qs = Parent.objects.filter(name="hoge").prefetch_related( Prefetch( "children_set", queryset=Children.objects.select_related("school"), ) ) for parent in parent_qs: my_children = parent.children_set.all() for my_child in my_children print(my_child.school.school_name)
順参照ー逆参照の組合せ
順参照は select_related (INNER JOIN), 逆参照は prefetch_related (2 つの SQL クエリーをメモリ内で Django が連結) で対応するわけだが、組み合わせたいときはそのパターンによって書き方がある。
以下のサイトで色々な組合せが書いてあるが
Django ORM にて SQL発行数を抑えながら順参照、逆参照する方法
順参照-逆参照のパターンが無かったのでここに記す。
prefetch_related("foo__bar_set")
もし filter を使い、to_attr に格納するなら foo を経由して呼び出すことになる。
例えば上記サイトのモデルをベースに、Department への順参照をする Asset というモデル (Asset の FK として department_id ) があるとする。
employees = Employee.objects.all().prefetch_related( "department__asset_set", queryset=Asset.objects.filter(name__icontains="テスト"), to_attr="assets" )
こうした場合、assets には Asset のインスタンスの list が格納されている。(queryset ではない)
このインスタンスの list を呼び出すには
for employee in employees: my_asset_list = employee.department.assets
と department を経由して assets を呼び出す。
コメント