この記事について
Django REST framework に検索機能を楽に追加できるプラグイン「django-filter」の使い方をまとめました。
django-filter とは
少ないコーディングで検索機能を追加するdjangoのプラグインです。
組み込みのFilterSetより柔軟な設定が可能です。
公式ドキュメント
https://django-filter.readthedocs.io/en/master/
Django REST framework に触れていない人は、まずこちらのチュートリアル記事からどうぞ。
Django REST Frameworkを使って爆速でAPIを実装する
サンプルコード
チュートリアルのサンプルコードを省略したものを使います。
パッケージのバージョン
Django (2.0.1)
django-crispy-forms (1.7.0)
django-filter (1.1.0)
djangorestframework (3.7.7)
pip install
でよしなに準備します。
INSTALLED_APPS = [
...
'rest_framework',
'django_filters',
'crispy_forms',
'sample',
]
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}
from django.db import models
# Create your models here.
# テスト結果の記録をイメージ
class Exam(models.Model):
LEVEL_SET = (
('easy', "初級"),
('normal', "中級"),
('hard', "上級"),
)
fullname = models.CharField(max_length=32)
level = models.CharField(choices=LEVEL_SET, default='easy', max_length=8)
score = models.IntegerField()
is_checked = models.BooleanField()
created_at = models.DateTimeField(auto_now_add=True)
# Create your views here.
from django_filters import rest_framework as filters
from rest_framework import viewsets
from rest_framework import serializers
from .models import Exam
class ExamSerializer(serializers.ModelSerializer):
class Meta:
model = Exam
fields = ('__all__')
class ExamViewSet(viewsets.ModelViewSet):
queryset = Exam.objects.all()
serializer_class = ExamSerializer
from django.conf.urls import url, include
from django.contrib import admin
from rest_framework import routers
from sample.views import ExamViewSet
router = routers.DefaultRouter()
router.register(r'exam', ExamViewSet)
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/', include(router.urls)),
]
Browsable API画面
フィルタ機能が有効になると、ブラウザでWebAPIに直接アクセスしたときに出てくるBrowsable API 画面 に「Filter」ボタンが追加されます。ここをクリックすると検索用ウィンドウからAPIを試すことができます。
※crispy_formsを追加しておくと、ここのフォームがbootstrap対応になります。
文法
フィルタセットの文法
ViewSet(WebAPI)にfilter_fieldsを定義して簡潔に使う方法もありますが、完全一致の検索にしか使えません。
部分一致や範囲を指定するにはFilterSetを継承したフィルタセットを作成し、ViewSetのfilter_classで読みこみます。
from django_filters import rest_framework as filters
from rest_framework import viewsets
from rest_framework import serializers
from .models import Exam
# FilterSetを継承したフィルタセット(設定クラス)を作る
class ExamFilter(filters.FilterSet):
# フィルタの定義
fullname = filters.CharFilter(name="fullname", lookup_expr='contains')
score_gt = filters.NumberFilter(lookup_expr='gt')
class Meta:
model = Exam
# フィルタを列挙する。
# デフォルトの検索方法でいいなら、モデルフィールド名のフィルタを直接定義できる。
fields = ['fullname', 'score_gt']
class ExamViewSet(viewsets.ModelViewSet):
queryset = Exam.objects.all()
serializer_class = ExamSerializer
# フィルタセットの指定
filter_class = ExamFilter
フィルタの定義
(公式) https://django-filter.readthedocs.io/en/latest/ref/filters.html#filters
基本文法
フィルタ名= filters.CharFilter(name="fullname", lookup_expr='contains')
・name: モデル内の検索対象フィールド ※デフォルトはフィルタ名
・lookup_expr 検索条件を指定する ※デフォルトはフィルタクラスによって異なる
・method: 独自の検索処理を定義できる(後述)
lookup_exprはdjangoのクエリ構文と同じ表現で検索方法を指定する。
####lookup_exprに指定できる値の一覧(一部)
・テキスト
lookup_expr | 検索方法 |
---|---|
exact | 完全一致 |
contains | 部分一致 |
startswith | 前方一致 |
endswith | 後方一致 |
regex | 正規表現 |
・数値・日時・テキスト(アスキー順)
lookup_expr | 検索方法 |
---|---|
gt | 超 |
lt | 未満 |
gte | 以上 |
lte | 以下 |
日時
・時刻の一部に一致する検索が可能
lookup_expr | 検索方法 |
---|---|
date | 日付(YYYY-M-D) |
year | 年(YYYY) |
month | 月(M) |
day | 日(D) |
week_day | (1-7 日曜が1) |
hour | 時(H) |
minute | 分(m) |
second | 秒(s) |
##主なフィルタの種類
よくつかいそうなフィルタの定義例。これ以外にも多くの種類がある。詳しくは公式参照。
https://django-filter.readthedocs.io/en/latest/ref/filters.html#filters
###CharFilter
テキスト検索
fullname = filters.CharFilter(lookup_expr='contains')
###NumberFilter
数値検索
score = filters.NumberFilter(lookup_expr='gt')
###BooleanFilter
Bool値検索
is_checked = filters.BooleanFilter()
※パラメータはtrueかfalseを指定
###DateTimeFilter
日付検索
created_at = filters.DateTimeFilter(lookup_expr='gt')
パラメータは「YYYY-MM-DD HH24-mm-SS」または「YYYY-MM-DD」で指定
###RangeFilter
範囲検索
score_range = filters.RangeFilter(name='score')
フィルタ名に「_0」「_1」がついたパラメータが追加され、それぞれ開始値と終了値を扱う。
片方だけ設定してもOK。
ChoiceFilter
選択肢検索
level = filters.ChoiceFilter(choices=Exam.LEVEL_SET)
値のエイリアスで検索する。choicesの指定にはModelのchoicesの設定を流用する。
MultipleChoiceFilter
level = filters.MultipleChoiceFilter(choices=Exam.LEVEL_SET)
同名のパラメータを複数回使ったOR検索が可能になる
並べ替え
OrderingFilterを使って並べ替え(ソート順)の指定ができる。
フォート方法のフィールドはfieldsに(モデルフィールド,パラメータ名)のタプル配列で指定する
order_by = filters.OrderingFilter(
# tuple-mapping retains order
fields=(
('fullname', 'fullname'),
('score', 'score_sort'),
),
)
残念ながら複数のフィールドを使った並べ替えはできないので、やりたい時は下のカスタマイズ法でやる
独自の検索処理を行う方法
フィールドの method 要素を使って独自のフィルター処理を定義できる。
以下は並べ替えた後、指定した数だけ上位件数を取得する例
class ExamFilter(filters.FilterSet):
def get_top(self, queryset, name, value):
return queryset.all()[:value]
order_by = filters.OrderingFilter(
# tuple-mapping retains order
fields=(
('fullname', 'fullname'),
('score', 'score'),
),
)
top = filters.NumberFilter(method='get_top')
class Meta:
model = Exam
fields = ['order_by','top']