Django入門|OR 条件でクエリセットを取得する方法のイメージ画像

Django入門|OR 条件でクエリセットを取得する方法

  • 公開日:2018/05/11
  • 更新日:2019/01/13
  • 投稿者:n bit

Djangoで、サイト内の検索をOR条件で行いたい場合や、通常のget()や、filter()時よりもより複雑な条件でクエリセットを取得したい場合があります。そのような時にキーワード引数をOR条件で接続しクエリセットをフィルタリングして取得する方法を解説します。

  • Python
  • Django

この記事は約 分で読めます。(文字)

Djangoで、OR 条件でクエリセットを取得する

Djangoでは通常クエリセットを取得する時、引数に指定した項目はAND条件で接続されフィルタリングされます。つまり、「条件Aと条件Bが両方満たされる場合(AND条件)」と言う条件でクエリセットが取得されます。

しかし、「条件Aと条件Bのいずれかが満たされる場合(OR条件)」と言う条件でクエリセットを取得したい場合もありますよね。

そこで、今回はAND条件ではなくOR条件でクエリセットを取得する方法を解説します。OR条件でクエリセットを取得するためにはQ オブジェクトを利用します。Q オブジェクトは1つずつのキーワード引数をまとめOR条件用の演算子で接続することができます。

以下のようなモデルからOR条件を使ってクエリセットを取得してみます。

models.py

class Tag(models.Model):

name = models.CharField("タグ名", max_length=255)
url = models.CharField("URL", max_length=255, unique=True)

class Meta:
verbose_name_plural = 'タグ'

def __str__(self):
return self.name


class Post(models.Model):
url = models.CharField("URL", max_length=255, unique=True)
title = models.CharField("タイトル", max_length=255)
text = models.TextField("本文")

is_public = models.BooleanField("公開可能か?", default=False)
created_at = models.DateTimeField("作成日", auto_now_add=True)
updated_at = models.DateTimeField("更新日", auto_now=True)

owner_user = models.ForeignKey(User, verbose_name='オーナーユーザ', default=1, on_delete=models.SET_DEFAULT)
post_tag = models.ManyToManyField(Tag, blank=True, verbose_name='タグ')

class Meta:
verbose_name_plural = 'ページ'

def __str__(self):
return self.title

AND 条件でのクエリセット取得

title(タイトル)、text(本文)、post_tag(タグ)、の全てにキーワード「hoge」が含まれているレコードを抽出する場合のフィルタリング設定方法は下記のようになります。これはいつものAND条件でクエリセットを取得しているときにする記述方法ですね。

s_text = 'hoge'

results = Post.objects.filter(
title__contains=s_text,
text__contains=s_text,
post_tag__name__contains=s_text
).distinct()

「__contains」は、キーワードの部分位置(一部に含まれている)を指定するためのも、「.distinct()」は、「hoge」が1部に含まれているレコードがその件数分ヒットしてしまうのでその重複を避けるための指定です。まぁ、3つものAND条件でフィルタリングしていますのでなかなか重複することが少ないようにも思いますが念のために記述しています。

OR 条件でクエリセットを取得する方法

これを、OR条件のtitle(タイトル)、text(本文)、post_tag(タグ)、のいずれかにキーワード「hoge」が含まれているレコードを抽出する場合のフィルタリング設定方法は下記のように変わります。最初にQ オブジェクトをインポートしていることに注意してください。

from django.db.models import Q


s_text = 'hoge'
results = Post.objects.filter(
Q(title__contains=s_text) |
Q(text__contains=s_text) |
Q(post_tag__name__contains=s_text)
).distinct()

OR条件でフィルタリングした場合はかなりの確率で同じレコードの重複が発生しますので「.distinct()」を必ず記述しておきましょう。

OR 条件とAND条件を複合したクエリセットの取得する方法

今度はさらに条件を加えてOR 条件とAND条件を複合したクエリセットの取得を行う方法です。先程のOR条件にユーザ"A"さんが保有しているPostであるかどうかの条件を加えます。

「タイトルにテキスト"hoge"が含まれていてかつユーザが"A"さんの場合」、または、「本文にテキスト"hoge"が含まれていてかつユーザが"A"さんの場合」、または、「タグ名にテキスト"hoge"が含まれていてかつユーザが"A"さんの場合」と言うOR 条件とAND条件の複合条件になります。

ユーザ"A"さんのpkが1の場合

from django.db.models import Q


s_text = 'hoge'
owner_user = User.objects.get(pk=1)
results = Post.objects.filter(
Q(title__contains=s_text), Q(owner_user=owner_user) |
Q(text__contains=s_text), Q(owner_user=owner_user) |
Q(post_tag__name__contains=s_text), Q(owner_user=owner_user)
).distinct()

Q()でくくったキーワード引数を「,」で接続しています。これは通常のAND条件の記述方法と同じですね。つまり、Q()でくくったキーワード引数をAND条件の演算子「,」とOR条件の演算子「|」で接続していくことで好きな条件を作ることができます。

今日のdot

DjangoでOR条件を使ったフィルタリングでクエリセットを取得するにはQ オブジェクトを利用します。Q オブジェクトは複雑な検索条件でフィルタリングすることが可能でOR条件だけではなくAND条件を組み合わせたフィルタリングもできるようになっています。