Prefetch オブジェクト, 順参照の逆参照について | SEの道標
django

Prefetch オブジェクト, 順参照の逆参照について

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 を呼び出す。

コメント

タイトルとURLをコピーしました