Django ORMでOR条件(いずれかの条件に一致)を使ったデータ取得方法を解説します。Qオブジェクトを使うと、filter()だけでは表現できない複雑な条件式を構築できます。
基本的な使い方
OR条件を使うには、django.db.models.Qをインポートして|(パイプ)演算子で条件を結合します。
from django.db.models import Q
from .models import Employee
def index(request):
# nameが"山田"またはageが30以上のEmployeeを取得
employees = Employee.objects.filter(
Q(name="山田") | Q(age__gte=30)
)
print(employees)<QuerySet [<Employee: 山田太郎>, <Employee: 佐藤花子>, <Employee: 田中一郎>]>Q(条件1) | Q(条件2)と書くと、SQLのWHERE 条件1 OR 条件2に相当するクエリが生成されます。
関連モデルのOR条件
関連モデルのフィールドに対してもOR条件を使えます。
from django.db.models import Q
from .models import Company
# Employeeのidが1〜10 または nameが"test" であるCompanyを取得
companies = Company.objects.filter(
Q(employee__id__range=(1, 10)) | Q(employee__name="test")
).distinct()関連モデルのフィールドを使う場合は、ダブルアンダースコア記法と組み合わせます。.distinct()でJOINによる重複を排除しています。
3つ以上のOR条件
|演算子を連結して、3つ以上の条件をOR結合できます。
from django.db.models import Q
# 3つの条件のいずれかに一致するEmployeeを取得
employees = Employee.objects.filter(
Q(department="営業部") |
Q(department="技術部") |
Q(age__gte=40)
)OR条件とAND条件の組み合わせ
Qオブジェクトでは|(OR)、&(AND)、~(NOT)を組み合わせて複雑な条件を表現できます。
from django.db.models import Q
# (営業部 OR 技術部) AND 在籍中
employees = Employee.objects.filter(
(Q(department="営業部") | Q(department="技術部")) &
Q(status="在籍中")
)
# NOT条件: 営業部以外の社員
employees = Employee.objects.filter(
~Q(department="営業部")
)-- (営業部 OR 技術部) AND 在籍中
SELECT * FROM employee
WHERE (department = '営業部' OR department = '技術部')
AND status = '在籍中'括弧を使ってグループ化することで、条件の優先順位を制御できます。
動的にOR条件を構築する
検索条件が動的に変わる場合は、ループでQオブジェクトを組み立てます。
from django.db.models import Q
from functools import reduce
import operator
# 検索キーワードをOR条件で検索
keywords = ["山田", "佐藤", "田中"]
# 方法1: reduceを使う
q_objects = [Q(name__contains=kw) for kw in keywords]
employees = Employee.objects.filter(
reduce(operator.or_, q_objects)
)
# 方法2: ループで構築
query = Q()
for kw in keywords:
query |= Q(name__contains=kw)
employees = Employee.objects.filter(query)どちらの方法もWHERE name LIKE '%山田%' OR name LIKE '%佐藤%' OR name LIKE '%田中%'と同等のSQLが生成されます。
実践的な使用例
検索フォームからの入力をOR条件で処理する実践例です。
from django.shortcuts import render
from django.db.models import Q
from .models import Employee
def employee_search(request):
employees = Employee.objects.all()
search = request.GET.get('search', '')
if search:
# 名前またはメールアドレスで検索
employees = employees.filter(
Q(name__contains=search) |
Q(email__contains=search)
)
department = request.GET.get('department')
if department:
# さらにAND条件で部署を絞り込む
employees = employees.filter(department=department)
return render(request, 'employees/search.html', {
'employees': employees,
'search': search,
})filter()に複数の引数を渡すとAND条件になります。OR条件を使いたい場合は必ずQオブジェクトを使いましょう。filter(name="A", age=20)はAND、filter(Q(name="A") | Q(age=20))はORです。
Qオブジェクトと通常のキーワード引数を混在させる場合、Qオブジェクトを先に記述する必要があります。filter(Q(name="A") | Q(name="B"), age=20)は正しいですが、順序を逆にするとエラーになります。
まとめ
Q(条件1) | Q(条件2)でOR条件を表現する&でAND、~でNOT条件も組み合わせられる- 関連モデルのフィールドにもダブルアンダースコア記法でOR条件が使える
- 動的な条件構築には
reduce(operator.or_, q_list)が便利 Qオブジェクトと通常引数を混在させる場合はQを先に書く