🗄️

Django ORM内部 — QuerySetがlazyな理由

filter().exclude().order_by()をチェーンしてもSQLが出ない原理

QuerySetは「SQLクエリを説明するオブジェクト」であって実行結果ではない。

内部構造

QuerySet内部にquery属性がある。django.db.models.sql.QueryオブジェクトでWHERE条件、ORDER BY、JOIN情報をツリー構造で保管。filter()は新QuerySetを複製(_clone())し、query.add_q()でQ オブジェクトを条件ツリーに追加。SQL文字列はまだ生成されない。

SQL生成はquery.get_compiler().as_sql()呼び出し時 — QuerySetが「評価」される時(__iter____len__list()等)。

なぜlazyか

  1. チェーン最適化 — 複数条件を積んでSQL1回だけ実行
  2. 再利用 — 同じQuerySetに異なる条件を追加して複数クエリ作成可能
  3. 不要クエリ防止 — 評価しなければSQLが出ない

N+1問題

select_related(JOIN)またはprefetch_related(別クエリ+Python組み合わせ)で解決。

キーポイント

1

filter/exclude/order_byはSQLを実行しない — 条件を積むだけ

2

list()、for、[0]等でアクセスした時初めてSQL実行(lazy evaluation)

3

内部的にQueryオブジェクトが条件ツリーを管理→as_sql()でSQL文字列生成

4

N+1はselect_related(JOIN)またはprefetch_related(別クエリ)で解決

ユースケース

複雑な検索 — 条件付きfilterをチェーンで組み合わせ APIページネーション — QuerySetスライスでLIMIT/OFFSET SQL生成

参考資料