🗄️
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か
- チェーン最適化 — 複数条件を積んでSQL1回だけ実行
- 再利用 — 同じQuerySetに異なる条件を追加して複数クエリ作成可能
- 不要クエリ防止 — 評価しなければ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生成