概要
Django REST frameworkではdjango-filterを使うと簡単に検索機能を追加できます
今回は
- インストールの方法
- filterの設定方法
- Swaggerでの使い方
について解説していきたいと思います
デフォルトで使えるBrowzableAPIでもFilterは使えますがSwaggerを使ってみたい方は以下の記事を参考に設定してみてください
必要な設定
まずはdjango-filterをインストールします
pip install django-filter
次にsettings.pyを編集します
INSTALLED_APPS = [
"rest_framework",
"drf_spectacular",
'django_filters',
]
REST_FRAMEWORK = {
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
'DEFAULT_FILTER_BACKENDS': (
'django_filters.rest_framework.DjangoFilterBackend',
),
}
実際に設定してみよう!
Model
今回はCustomerとWorkplaceのModelを作成します
CustomerとWorkplaceとの関係は1対1です
# お客様
class Customer(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
kana = models.CharField(max_length=50)
name = models.CharField(max_length=50)
age = models.IntegerField()
post_no = models.CharField(
max_length=7, validators=[RegexValidator(r"^[0-9]{7}$", "7桁の数字を入力してください。")]
)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = "Customer"
# 勤務先
class Workplace(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
customer = models.OneToOneField(Customer, on_delete=models.CASCADE,related_name="workplace")
kana = models.CharField(max_length=255)
name = models.CharField(max_length=255)
phone_no = models.CharField(
max_length=11,
validators=[RegexValidator(r"^[0-9]{10,11}$","10か11桁の数字を入力してください。")],
)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = "Workplace"
Serilaizer
to_representationについてもっと知りたい方は以下の記事を参考にしてください
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = ["id","kana","name","age","post_no","created_at"]
read_only_fields = ["id","created_at"]
def to_representation(self, instance):
ret = super(CustomerSerializer, self).to_representation(instance)
# 勤務先インスタンス
workplace = instance.workplace
# 勤務先名
ret["workplace_name"] = workplace.name
return ret
class WorkplaceSerializer(serializers.ModelSerializer):
class Meta:
model = Workplace
fields = ["id","kana","name", "phone_no","created_at"]
read_only_fields = ["id","created_at"]
def to_representation(self, instance):
ret = super(WorkplaceSerializer, self).to_representation(instance)
# お客様インスタンス
customer = instance.customer
# お客様名
ret["customer_name"] = customer.name
return ret
Filter
今回は以下の絞り込み機能を実装します
- お客様一覧
- お客様名
- 勤務先名
- 勤務先一覧
- お客様名
- 勤務先名
- データの作成日
import django_filters
from relationships.models import (
Customer,
Workplace,
)
# お客様Filter
class CustomerFilter(django_filters.FilterSet):
name = django_filters.CharFilter(field_name="name",lookup_expr="contains")
workplace = django_filters.CharFilter(field_name="workplace__name",lookup_expr="contains")
class Meta:
model = Customer
# フィルタを列挙する
fields = ["name","workplace"]
# 勤務先Filter
class WorkPlaceFilter(django_filters.FilterSet):
customer = django_filters.CharFilter(field_name="customer__name",lookup_expr="contains")
name = django_filters.CharFilter(field_name="name",lookup_expr="contains")
created_at = django_filters.DateTimeFromToRangeFilter()
class Meta:
model = Workplace
fields = ["customer","name","created_at"]
View
今回はCustomerとWorkplaceのViewを作成します
from rest_framework import viewsets
# settings.pyで定義したDjangoFilterBackendをimport
from django_filters.rest_framework import DjangoFilterBackend
from .models import (
Customer,
Workplace,
)
# 先ほど作成したFilterをimport
from .filters import (
CustomerFilter,
WorkPlaceFilter,
)
# お客様ViewSet
class CustomerViewSets(viewsets.ModelViewSet):
queryset = Customer.objects.all()
serializer_class = CustomerSerializer
# settings.pyで設定したDjangoFilterBackendを追加
filter_backends = (filters.DjangoFilterBackend,)
# 自身で定義したFilterSetクラスを指定
filterset_class = CustomerFilter
# 勤務先ViewSet
class WorkplaceViewSets(viewsets.ModelViewSet):
queryset = Workplace.objects.all()
serializer_class = WorkplaceSerializer
filter_backends = (DjangoFilterBackend,)
filterset_class = WorkPlaceFilter
勤務先一覧を絞り込んでみよう!
テキストボックスに値を指定せずにGETすると以下のように勤務先一覧のレスポンスが帰ってきます
勤務先名で絞り込み
すると、リクエストURLにnameのクエリパラメータが表示され、
lookup_expr="contains"
をFilterに指定したことでnameが四角
と部分一致するWorkplaceの一覧をGETすることに成功しました
お客様名で絞り込み
WorkplaceにはcustomerのForeignKeyも持っています
ForeignKeyでつながっているModelのフィールドは
field_name="customer__name"
というふうにForeignKeyの後にアンダースコアを2つ(__)書いて、
その後に任意のフィールドを指定すると取得できます
すると、リクエストURLにcustomerのクエリパラメータが表示され、customerが一郎
と部分一致するWorkplaceの一覧をGETすることに成功しました
作成日で絞り込み
created_at_beforeに入力するとその日付以前のデータが、
created_at_afterに入力するとその日付以後のデータが表示されます
すると、リクエストURLにcreated_atのクエリパラメータが表示され、created_atが7月10日
以前のWorkplaceの一覧をGETすることに成功しました
すると、リクエストURLにcreated_atのクエリパラメータが表示され、created_atが7月20日
以後のWorkplaceの一覧をGETすることに成功しました
お客様一覧を絞り込んでみよう!
テキストボックスに値を指定せずにGETすると以下のようにお客様一覧のレスポンスが帰ってきます
続いて勤務先名を絞り込んでみましょう
Workplaceと違ってCustomerにはworkplaceのForeignKeyを持っていません
そこで思い出してほしいのですがWorkplaceのModelでrelated_nameを指定したのでCustomerはWorkplaceを逆参照することができます
customer = models.OneToOneField(Customer, on_delete=models.CASCADE,related_name="workplace")
逆参照することでworkplaceのインスタンスをWorkplaceSerilaizer内で作成できるようになります
def to_representation(self, instance):
ret = super(CustomerSerializer, self).to_representation(instance)
# 勤務先インスタンス
workplace = instance.workplace
# 勤務先名
ret["workplace_name"] = workplace.name
return ret
そうすることで
field_name="workplace__name"
というふうにinstance名の後にアンダースコアを2つ(__)書いて、
その後に任意のフィールドを指定すると取得できます
すると、リクエストURLにworkplaceのクエリパラメータが表示され、workplace_nameが丸々
と部分一致するCustomerの一覧をGETすることに成功しました
まとめ
django-filterを使うと手軽に検索・絞り込み機能が使えるのでいいですね
Filterの詳細は公式ドキュメントに全て書いてあるので確認してみましょう
参考