まず初めに今回使用するモデルを紹介
前提として、以下のような Model クラスに対する DB 検索を行うものとします。
from django.db import models
class BookManager(models.Manager):
pass
class Book(models.Model):
name = models.CharField(
verbose_name=("名称"),
max_length=255
)
class = models.IntegerField(
verbose_name=('分類'),
choices=(
(1, '技術'),
(2, '芸術'),
(3, 'スポーツ')
....
)
)
create_at = models.DateField(
verbose_name=("作成日時")
)
objects = BookManager()
まずは基本的なところから
all
all で全件取得します。QuerySet を返します。
# 全レコードを取得したい場合
books = Book.objects.all()
# SQL だとこんな感じ
select * from books;
get
get で 1 件だけ取得ができます。この例だと Book オブジェクトを返します。
# 1 件だけ取得したい場合
book = Book.objects.get(pk=1)
# PK の名称を指定(ここでは id)しても同じ結果となる
# 基本的には上の pk= の方を使用するのがいいと思う
book = Book.objects.get(id=1)
# SQL だとこんな感じ
select * from books where id = 1;
get で取得する際、存在しない / 結果が複数存在する条件を指定した場合エラーになってしまうので注意が必要です。
# 存在しない条件
Book.objects.get(id=-1) # DoesNotExist 例外が発生する
# 複数存在する条件
Book.objects.get(name='test') # MultipleObjectsReturned 例外が発生する
filter
与えられた条件にマッチするレコードを取得します。
複数の条件を指定することができますが、その場合 AND で条件が設定されます。
exclude
与えられた条件にマッチしないレコードを取得します。
複数の条件が指定された場合、AND で結合してそれ全体をNOTで囲います。
# name が django ではない
books = Book.objects.exclude(name='django')
# SQL だとこんな感じ
select * from books where not (name = 'django')
検索条件
filetr などに設定できる条件を解説します。
完全一致
[ カラム名=条件 ]としたり、 [ カラム名__exact=条件 ] とすることで完全一致の条件になります。
= で指定
# 完全一致
books = Book.objects.filter(name='django')
# SQL だとこんな感じ
select * from books where name = 'django'
カンマ区切りで複数の条件を設定することもできます。
# 完全一致(カンマ区切りで複数条件)
books = Book.objects.filter(id=1, name="django")
# SQL だとこんな感じ
select * from books where id = 1 and name = 'django'
exact
自分はあまり使わないですが、以下も完全一致の書き方になります。
# 完全一致
books = Book.objects.filter(id__exact=1)
# SQL だとこんな感じ
select * from books where id = 1
Noneを指定するとNullとして解釈されます。
# 完全一致(Noneを指定)
books = Book.objects.filter(name__exact=None)
# SQL だとこんな感じ
select * from books where name is NULL
ただ、Nullを条件に含めるのであれば、isnullを使用する方が一般的かと思います。
isnull
# Null であるか
books = Book.objects.filter(name__isnull=True)
# SQL だとこんな感じ
select * from books where name is NULL
Falseを指定することで is not NULL となります。
# Null でないか
books = Book.objects.filter(name__isnull=False)
# SQL だとこんな感じ
select * from books where name is not NULL
部分一致
SQLでいうところの LIKE にあたるものです。
contains, icontains
は、指定の文字列を含むかをチェックする条件です。contains
大文字小文字を区別します。
# test を含むか(大文字小文字を区別する)
books = Book.objects.filter(name__contains='test')
# SQL だとこんな感じ
select * from books where name LIKE '%test%'
icontains の場合は大文字小文字を区別しません。
# test を含むか(大文字小文字を区別しない)
books = Book.objects.filter(name__icontains='test')
# SQL だとこんな感じ
select * from books where name ILIKE '%test%'
startswith, istartswith
は、指定の文字列から始まるかをチェックする条件です。startswith
istartswith
だと大文字小文字を区別しません。
# test で始まるか(大文字小文字を区別する)
books = Book.objects.filter(name__startswith='test')
# SQL だとこんな感じ
select * from books where name ILIKE 'test%'
endswith, iendswith
は、指定の文字列で終わるかをチェックする条件です。endswith
iendswith
だと大文字小文字を区別しません。
# test で始まるか(大文字小文字を区別する)
books = Book.objects.filter(name__endswith='test')
# SQL だとこんな感じ
select * from books where name ILIKE '%test'
正規表現による一致
regex, iregex
正規表現を使用することもできます。
iregex では大文字小文字を区別しません。
# start で始まって、end で終わる文字列を検索
Book.objects.filter(name__regex=r'^start.*end$')
実行されるクエリはデータベースごとに異なります。
# PostgreSQL の場合
SELECT * FROM books WHERE name ~ '^word.*press$';
# MySql の場合
SELECT * FROM books WHERE name REGEXP '^word.*press$';
数値系
gt(greater than), gte(greater than or equal to)
gt は指定の数値より大きいかをチェックする条件です。
gte だと指定の数値以上かをチェックする条件になります。
# id が 5 より大きい
books = Book.objects.filter(id__gt=5)
# SQL だとこんな感じ
select * from books where id > 5
lt(less than), lte(less than or equal to)
lt は指定の数値未満かをチェックする条件です。
lte だと指定の数値以下かをチェックする条件になります。
# id が 5 未満
books = Book.objects.filter(id__lt=5)
# SQL だとこんな感じ
select * from books where id < 5
in
指定されたリストなどに含まれるかをチェックします。
文字列を指定することも可能です。
book = Book.objects.filter(id__in=[1, 3, 4])
book = Book.objects.filter(name__in="test")
SELECT * FROM books WHERE id IN (1, 3, 4);
SELECT * FROM books WHERE name IN ('t', 'e', 's', 't');
range
数値だけでなく、日付や文字列でも使用できます。
# id が 1 ~ 5
books = Book.objects.filter(id__range=(1, 5))
# SQL だとこんな感じ
select * from books where id between 1 and 5
日付系
USE_TZ が True の場合、日付フィールドはフィルタリングの前にカレントタイムゾーンに変換されます。
これにはデータベースの タイムゾーン定義 が必要です。
date, time
datetime のフィールドをそれぞれ date, time としてキャストします。
Book.objects.filter(ceated_at__date=datetime.date(2005, 1, 1))
year
“年” の完全一致で検索します。
# 日付の"年"が完全一致
books = Book.objects.filter(create_at__year=2025)
# SQL だとこんな感じ
select * from books where create_at between '2025-01-01' and '2025-12-31'
他の条件(例えば”以上”を表すgte)と組み合わせて使用することもできます。
# 日付の"年"が完全一致
books = Book.objects.filter(create_at__year__gte=2025)
# SQL だとこんな感じ
select * from books where create_at >= '2025-01-01'
month, day, hour, minute, second
“月” や “日” などを完全一致で検索します。
同じような解説になるのでまとめます。
# 日付の"月"が完全一致
books = Book.objects.filter(create_at__month =12)
# SQL だとこんな感じ
select * from books where EXTRACT('month' FROM create_at) = 12
week_day
“曜日” の一致で検索します。
1 (日曜日) から 7 (土曜日) までの曜日を表す整数値を指定します。
book = Book.objects.filter(created_at__week_day=2)
week
ISO-8601 に従った週番号 (1~52または53) を返します。
quarter
“四半期” の一致。年の四半期を表す 1 から 4 までの整数値を指定します。
第1四半期(1月1日から3月31日まで)
第2四半期(4月1日から6月30日まで)
第3四半期(7月1日から9月30日まで)
第4四半期(10月1日から12月31日まで)
# 第2四半期(4月1日から6月30日まで)を検索
book = Book.objects.filter(created_at__quarter=2)
iso_year, iso_week_day
ISO 8601の週番号と年、曜日との完全一致で検索します。
QuerySet について
QuerySet は遅延評価になるため、all とか filter とかの時点ではクエリは実行されません。
for で回すとか、list()を呼び出すとかした時にクエリを実行して結果を取得します。
公式サイト:https://docs.djangoproject.com/ja/5.1/ref/models/querysets/#when-querysets-are-evaluated
# イテレートした時にクエリを実行する
for book in Book.objects.all():
print(book.name)
# list を呼び出すことでクエリを実行する
book_list = list(Book.objects.all())
余談
filter は QuerySet を戻り値として返すため、その QuerySet に対してさらに filter をするなんてこともできてチェーンメソッドのような使い方もできます。
その場合もやはり AND で繋がるので注意です。
私は django で検索機能を実装する時、以下のような形で実装しています。
def search(self, search_conditions):
# まずは全件取得
books = Book.objects.filter()
# 検索条件に name が含まれていれば条件を追加する
if search_conditions.get('name'):
books = books.filter(name=search_conditions.get('name'))
return books
次回は OR 条件で連結する方法であったり、集計関数なんかについてまとめようと思います。
コメント