Django ORM の filter メソッドをチェーンすると AND 条件になりますが、OR 条件や NOT 条件を表現するには Q オブジェクトが必要です。Q を使うことで、SQL の WHERE 句に相当する複雑な条件を Python らしく組み立てられます。
Q オブジェクトの基本
Q は django.db.models からインポートして使います。
from django.db.models import Q # OR 条件: タイトルに「Python」または「Django」を含む記事 articles = Article.objects.filter( Q(title__contains='Python') | Q(title__contains='Django') )
| が OR、& が AND、~ が NOT を表します。
| 演算子 | 意味 | SQL の対応 |
|---|---|---|
| | | OR | WHERE ... OR ... |
| & | AND | WHERE ... AND ... |
| ~ | NOT | WHERE NOT ... |
通常の filter チェーンは AND しか書けませんが、Q を使えば柔軟に条件を組み合わせられます。
OR 条件
「どちらかの条件に一致すればよい」場合は | で結合します。
# 下書きまたはカテゴリが「技術」の記事 articles = Article.objects.filter( Q(is_draft=True) | Q(category='技術') )
生成される SQL は次のようになります。
SELECT * FROM article WHERE is_draft = TRUE OR category = '技術';
NOT 条件
~ を付けると条件を反転できます。exclude で書ける場面もありますが、他の条件と組み合わせるときは ~Q の方が見通しがよくなります。
# カテゴリが「雑記」でなく、かつ公開済みの記事 articles = Article.objects.filter( ~Q(category='雑記') & Q(is_draft=False) )
複数の条件を組み合わせる
Q オブジェクト同士はネストできるため、括弧で優先順位を制御しながら複雑な条件を表現できます。
# (タイトルに「Python」を含む OR カテゴリが「技術」) # AND 公開済み articles = Article.objects.filter( (Q(title__contains='Python') | Q(category='技術')) & Q(is_draft=False) )
Python の演算子優先順位に従って評価されるため、& は | よりも先に結合します。意図を明確にするためにも、括弧を明示的に付ける方が安全です。
Q と通常のキーワード引数を混在させる
filter の中で Q オブジェクトと通常のキーワード引数を一緒に使うことも可能です。ただし、Q オブジェクトを先に書くというルールがあります。
# Q オブジェクトが先、キーワード引数が後 articles = Article.objects.filter( Q(title__contains='Python') | Q(title__contains='Django'), is_draft=False )
キーワード引数を Q より先に書くと SyntaxError になります。Python の仕様として、位置引数はキーワード引数の前に書かなければならないためです。
AND 条件のみ。シンプルな条件なら十分
OR・NOT を含む複雑な条件に対応。演算子で柔軟に組み立てられる
Q オブジェクトを動的に組み立てる
検索フォームなどでユーザーの入力に応じて条件を動的に追加したい場面では、Q オブジェクトをループで結合できます。
from functools import reduce from operator import or_ keywords = ['Python', 'Django', 'Flask'] conditions = [Q(title__contains=kw) for kw in keywords] combined = reduce(or_, conditions) articles = Article.objects.filter(combined)
reduce と or_ を使って、リスト内の Q オブジェクトをすべて OR で結合しています。and_ に変えれば AND 条件にもなります。
単純な AND 条件だけなら filter のチェーンで十分ですが、OR や NOT が絡む条件、あるいは動的に条件を組み立てる必要がある場面では Q オブジェクトが不可欠です。Django ORM を使いこなすうえで、確実に押さえておきたい機能と言えます。