ORM

Django ORMのOR条件入門|Qオブジェクトで複数条件検索

Django ORMでOR条件(いずれかの条件に一致)を使ったデータ取得方法を解説します。Qオブジェクトを使うと、filter()だけでは表現できない複雑な条件式を構築できます。

基本的な使い方

OR条件を使うには、django.db.models.Qをインポートして|(パイプ)演算子で条件を結合します。

views.py
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条件を使えます。

views.py
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結合できます。

views.py
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)を組み合わせて複雑な条件を表現できます。

views.py
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="営業部")
)
発行されるSQL
-- (営業部 OR 技術部) AND 在籍中
SELECT * FROM employee
WHERE (department = '営業部' OR department = '技術部')
  AND status = '在籍中'

括弧を使ってグループ化することで、条件の優先順位を制御できます。

動的にOR条件を構築する

検索条件が動的に変わる場合は、ループでQオブジェクトを組み立てます。

views.py
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条件で処理する実践例です。

views.py
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を先に書く