BLOGサブスレッドの日常
2022.02.16
djangorestframework で django-filter を使ってみた
torikai
鳥飼です。
django って QuerySet が結構便利で、基本的には別のパッケージで拡充する用事はあまり無いかなーと思うんですが、djangorestframework を使った時に django-filter に触れる機会があったので、かんたんな使い方を紹介したいと思います。
django-filter の何が嬉しいか
よくある検索機能を少ないコーディングで実装できるのが、django-filter の嬉しいところ。
クラスとして分離するので、他の機能から同じ検索処理を呼び出したい、という場合にも再利用がしやすいところも、良いところのひとつです。
準備
適当に model, viewset, serializer を用意。
# models.py
from django.db import models
class Author(models.Model):
name = models.TextField('投稿者名')
email = models.TextField('メールアドレス')
def __str__(self):
return self.name
class Article(models.Model):
subject = models.TextField('題名')
body = models.TextField('本文')
author = models.ForeignKey(Author, verbose_name='投稿者', on_delete=models.CASCADE)
created_at = models.DateTimeField('登録日時', auto_now_add=True)
def __str__(self):
return f'{self.subject} @{self.author}'
# views.py
from rest_framework import viewsets
from .filters import ArticleFilterSet
from .models import Article
from .serializers import ArticleSerializer
class ArticleViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Article.objects.prefetch_related('author')
serializer_class = ArticleSerializer
# serializers.py
from rest_framework import serializers
from .models import Author, Article
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ('name', )
class ArticleSerializer(serializers.ModelSerializer):
author = AuthorSerializer()
class Meta:
model = Article
fields = ('subject', 'body', 'author')
django-filter を導入
何はともあれ django-filter をインストール。
pip install django-filter
settings の INSTALLED_APPS、REST_FRAMEWORK に django-filter の設定を追加します。
# settings.py
...
INSTALLED_APPS = [
...
'django.contrib.staticfiles',
'django_filters', # 追加
'rest_framework',
'app.blog',
]
...
REST_FRAMEWORK = {
...
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend', # 追加
]
...
}
...
実際に使ってみる
ファイル名や配置場所は何でも良いんですが、今回は views.py などと同じ階層に filters.py を作って、その中にフィルタ設定を書いていきます。
# filters.py
from django_filters import rest_framework as filters
from .models import Article
class ArticleSearchFilterSet(filters.FilterSet):
subject = filters.CharFilter(field_name='subject', lookup_expr='contains')
body = filters.CharFilter(field_name='body', lookup_expr='contains')
author_name = filters.CharFilter(field_name='author', method='filter_author_name')
class Meta:
model = Article
fields = ('subject', 'body', 'author_name')
def filter_author_name(self, queryset, field_name, value):
_filters = {'author__name': value, 'author__email__contains': value}
return queryset.filter(**_filters)
本文や題名は部分一致で検索させたいので、lookup_expr に contains を指定。
ちなみに、lookup_expr のデフォルト値は expr なので、field_name='body__contains' のようには指定できません。
また、もし個別のフィルタリング処理をさせたい場合は、author_name のように method='filter_author_name' と指定して、同名の関数の中に処理を書けばOKです。
viewset を調整
viewset には filter_class を追加して、に先程のクラス(ArticleFilterSet)を指定すれば🙆
# views.py
from .filters import ArticleSearchFilterSet
...
class ArticleViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Article.objects.prefetch_related('author')
serializer_class = ArticleSearchFilterSet
filter_class = ArticleFilterSet # 追加
フィルタの処理をほぼ分離できるので、だいぶスッキリして良い感じですね!
また使う機会があれば使っていきたいと思えるパッケージでした。
この記事を書いた人
torikai
