LoginSignup
4
3

More than 5 years have passed since last update.

Djangoのmodelでの、eager loading機能であるprefetch_relatedに検索条件を追加する

Posted at

今更言うまでもないんですが、ORMってとっても便利ですよね。
pythonのフレームワークのDjangoにも実装されているんですが、便利がゆえに地雷になることもあったり。:volcano:
備忘録もかねて対応策を

やけに負荷のかかっているSQLが発生

all_prefetch.py
Foo.objects.filter(
    hoge=foo
).prefetch_related('bar')

上記のような一見、何の問題もなさそうなSQLを実行していたんですよ。
Fooテーブルから該当の条件のデータを取得して、1:N問題を避けるべく、relation関係にあるbarテーブルの内容をまとめて取得する……という。
ところが、何故かこの処理が呼び出されるAPIだけ処理が重いという事象が発生してみたので早速、調査を:thinking:

大量のデータを取得しているッ!?

check_all_prefetch.py
# ↑↑ この前にログイン中のユーザのデータを取得している

Foo.objects.filter(
    hoge=foo
).prefetch_related('bar') # <- barが1000件ぐらい存在していた

# ↓↓ そしてここより後の処理で、barから引っ張ってきたデータを、
# ユーザが持つ情報に応じてpython側でフィルタリングしている。

そう、SQL側でやるべきことをやっておらず、python側でやっていたッ!
データの転送量が多く、アクセスが殺到した際に問題になっていたという新事実が。
ありがちな問題……ではあるんですが、とはいえprefetch_relatedの部分に条件を設定できるような引数はなさそうです:sob:

prefetch_relatedに検索条件を追加するッ!

add_filter_prefetch.py
from django.db.models import Prefetch

# barのprefetch内容を設定
p = Prefetch('bar', Bar.objects.filter(id__in=user.hoge))

# prefetchに上記で作成したオブジェクトを渡す
Foo.objects.filter(
    hoge=foo
).prefetch_related(p)

こんな感じの修正になりました!
Prefetchを使用することでprefetch_relatedで生成されるSQLにも正しく検索条件を追加できています
これでpython側でフィルタリングするという不毛なこともなく、データの転送量も抑えらるようになりました:blush:

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3