Django ORM クエリの最適化

Django ORM はデータベース操作を容易にしますが、一方で危険も潜んでいます。関連オブジェクトの取得方法を誤ると、N+1 クエリ問題が発生します。

これは、例えば 100 件の投稿を取得する場合、アプリが 101 回のクエリを実行してしまう可能性があることを意味します。これにより、アプリの動作が著しく低下します。

これを解決するために、次の 2 つのツールを使用します。

  1. select_related

ForeignKey および OneToOneField に使用します。

SQL の JOIN を使用します。単一のクエリで関連データを取得します。

  • 例: Post は 1 人の Author を持つ。
  • 非効率な方法: 100 件の投稿に対して 101 回のクエリ。
  • 最適化された方法: .select_related("author") を使用して 1 回のクエリ。
  1. prefetch_related

ManyToManyField および逆方向の ForeignKey に使用します。

個別のクエリを実行し、Python 側でそれらを結合します。

  • 例: Post は多くの Tag を持つ。
  • 非効率な方法: 投稿用の 1 クエリ + 各投稿のタグ用のクエリが毎回実行される。
  • 最適化された方法: .prefetch_related("tags") を使用して合計 2 回のクエリ。

プロのヒント:

  • Prefetch オブジェクトを使用して、ループに入る前に関連データをフィルタリングします。
  • これらをチェーン(連結)できます。1 つの queryset 内で両方を使用することで、authors と tags を一度に取得できます。
  • ループ内で、prefetch されたリレーションシップに対して .filter() を呼び出すのは避けてください。これによりキャッシュがバイパスされ、再度データベースにアクセスしてしまいます。
  • select_related と .only() を併用して、必要なカラムのみを取得します。

まとめ:

  • ForeignKey / OneToOne: select_related を使用。
  • ManyToMany / Reverse FK: prefetch_related を使用。

django-debug-toolbar を使用してクエリ数を確認し、これらの問題を早期に発見しましょう。

Source: https://dev.to/fhva29/optimizing-django-orm-queries-a-practical-guide-to-selectrelated-and-prefetchrelated-1gpl