django-rest-framework 動的にserializerのフィールドを変更する


はじめに

django-rest-frameworkserializerを利用してクライアントからのリクエストに応じて動的にフィールドを変更する場合、
公式ガイドにあるクラスを利用し返すことが出来ます。

ビューセットからの呼び出しサンプルです。
ここではフィールドの動的変更のみを行いますが、他のパラメータと組み合わせることで返却するフィールドを柔軟に変更できるようになります。

Dynamic クラスの定義

公式ガイドにあるクラスを利用します。
https://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields

from rest_framework import serializers


class DynamicFieldsModelSerializer(serializers.ModelSerializer):
    """
    A ModelSerializer that takes an additional `fields` argument that
    controls which fields should be displayed.
    """

    def __init__(self, *args, **kwargs):
        # Don't pass the 'fields' arg up to the superclass
        fields = kwargs.pop('fields', None)

        # Instantiate the superclass normally
        super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)

        if fields is not None:
            # Drop any fields that are not specified in the `fields` argument.
            allowed = set(fields)
            existing = set(self.fields)
            for field_name in existing - allowed:
                self.fields.pop(field_name)

モデルの作成

シンプルなコード、名称を持つモデルです。

from uuid import uuid4
from django.db import models


class Customer(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
    code = models.CharField(verbose_name='コード', help_text='コード', max_length=10)
    name = models.CharField(verbose_name='名称', help_text='名称', max_length=50)

シリアライザの作成

class CustomerSerializer(DynamicFieldsModelSerializer):
    class Meta:
        model = Customer

        fields = (
            'id',
            'code',
            'name',
        )

ビューセットの作成

get_serializerをオーバーライドし、GETリクエスト時のみクエリ文字列に設定されたフィールド値で
取得する列を動的に変更します。

from rest_framework import viewsets


class CustomerViewSet(viewsets.ModelViewSet):
    queryset = Customer.objects.all()
    serializer_class = CustomerSerializer

    def get_serializer(self, *args, **kwargs):
        if self.action == 'list':
            if 'fields[]' in self.request.query_params:
                kwargs['fields'] = self.request.query_params.getlist('fields[]')

        return super().get_serializer(*args, **kwargs)

動作確認

フィールドを指定しない場合

curl -s -X GET "http://localhost:18000/api/customers/" -H "accept: application/json" | jq .
[
  {
    "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx1",
    "code": "001",
    "name": "test1"
  },
  {
    "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx2",
    "code": "002",
    "name": "test2"
  }
]

フィールドにcode, nameを指定した場合

curl -s -X GET "http://localhost:18000/api/customers/?fields[]=code&fields[]=name" --globoff -H "accept: application/json" | jq .
[
  {
    "code": "001",
    "name": "test"
  },
  {
    "code": "002",
    "name": "test2"
  }
]