Django ORM クエリの最適化
Django ORM はデータベース操作を容易にしますが、一方で危険も潜んでいます。関連オブジェクトの取得方法を誤ると、N+1 クエリ問題が発生します。
これは、例えば 100 件の投稿を取得する場合、アプリが 101 回のクエリを実行してしまう可能性があることを意味します。これにより、アプリの動作が著しく低下します。
これを解決するために、次の 2 つのツールを使用します。
- select_related
ForeignKey および OneToOneField に使用します。
SQL の JOIN を使用します。単一のクエリで関連データを取得します。
- 例: Post は 1 人の Author を持つ。
- 非効率な方法: 100 件の投稿に対して 101 回のクエリ。
- 最適化された方法: .select_related("author") を使用して 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 を使用してクエリ数を確認し、これらの問題を早期に発見しましょう。