1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

django-filterを実装して検索・絞り込みを簡単に実装しよう!

Posted at

概要

Django REST frameworkではdjango-filterを使うと簡単に検索機能を追加できます
今回は

  • インストールの方法
  • filterの設定方法
  • Swaggerでの使い方

について解説していきたいと思います
デフォルトで使えるBrowzableAPIでもFilterは使えますがSwaggerを使ってみたい方は以下の記事を参考に設定してみてください

必要な設定

まずはdjango-filterをインストールします

pip install django-filter

次にsettings.pyを編集します

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です

models.py
# お客様
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についてもっと知りたい方は以下の記事を参考にしてください

serilaizers.py
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

今回は以下の絞り込み機能を実装します

  • お客様一覧
    • お客様名
    • 勤務先名
  • 勤務先一覧
    • お客様名
    • 勤務先名
    • データの作成日
filters.py
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を作成します

views.py
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すると以下のように勤務先一覧のレスポンスが帰ってきます
スクリーンショット 2022-11-05 20.27.11.png

勤務先名で絞り込み

ここでnameに四角と指定してGETしてみます
スクリーンショット 2022-11-05 20.19.41.png

すると、リクエストURLにnameのクエリパラメータが表示され、

lookup_expr="contains"

をFilterに指定したことでnameが四角と部分一致するWorkplaceの一覧をGETすることに成功しました
スクリーンショット 2022-11-05 20.27.55.png

お客様名で絞り込み

WorkplaceにはcustomerのForeignKeyも持っています
ForeignKeyでつながっているModelのフィールドは

field_name="customer__name"

というふうにForeignKeyの後にアンダースコアを2つ(__)書いて、
その後に任意のフィールドを指定すると取得できます

ここでcustomerに一郎と指定してGETしてみます
スクリーンショット 2022-11-05 20.24.12.png

すると、リクエストURLにcustomerのクエリパラメータが表示され、customerが一郎と部分一致するWorkplaceの一覧をGETすることに成功しました
スクリーンショット 2022-11-05 20.25.12.png

作成日で絞り込み

created_at_beforeに入力するとその日付以前のデータが、
created_at_afterに入力するとその日付以後のデータが表示されます
スクリーンショット 2022-11-05 20.29.18.png

試しに7月10日以前のデータで絞ってみます
スクリーンショット 2022-11-05 20.32.32.png

すると、リクエストURLにcreated_atのクエリパラメータが表示され、created_atが7月10日以前のWorkplaceの一覧をGETすることに成功しました
スクリーンショット 2022-11-05 20.32.50.png

7月20日以後のデータで絞ってみます
スクリーンショット 2022-11-05 20.33.46.png

すると、リクエストURLにcreated_atのクエリパラメータが表示され、created_atが7月20日以後のWorkplaceの一覧をGETすることに成功しました
スクリーンショット 2022-11-05 20.34.37.png

お客様一覧を絞り込んでみよう!

テキストボックスに値を指定せずにGETすると以下のようにお客様一覧のレスポンスが帰ってきます
スクリーンショット 2022-11-05 19.00.35.png

続いて勤務先名を絞り込んでみましょう
Workplaceと違ってCustomerにはworkplaceのForeignKeyを持っていません
そこで思い出してほしいのですがWorkplaceのModelでrelated_nameを指定したのでCustomerはWorkplaceを逆参照することができます

models.py/Workplace
customer = models.OneToOneField(Customer, on_delete=models.CASCADE,related_name="workplace")

逆参照することでworkplaceのインスタンスをWorkplaceSerilaizer内で作成できるようになります

serializers.py/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つ(__)書いて、
その後に任意のフィールドを指定すると取得できます

ここでworkplaceに丸々と指定してGETしてみます
スクリーンショット 2022-11-05 19.07.09.png

すると、リクエストURLにworkplaceのクエリパラメータが表示され、workplace_nameが丸々と部分一致するCustomerの一覧をGETすることに成功しました
スクリーンショット 2022-11-05 19.08.21.png

まとめ

django-filterを使うと手軽に検索・絞り込み機能が使えるのでいいですね
Filterの詳細は公式ドキュメントに全て書いてあるので確認してみましょう

参考

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?