[PYTHON] django-rest-framework Dynamically change serializer fields

Introduction

When using serializer of django-rest-framework to dynamically change fields in response to client requests You can return by using the class in the official guide.

This is a sample call from the view set. Here, only the field is dynamically changed, but by combining it with other parameters, the returned field can be changed flexibly.

Dynamic class definition

Use the classes in the official guide. 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)

Creating a model

It is a model with a simple code and 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='code', help_text='code', max_length=10)
    name = models.CharField(verbose_name='name', help_text='name', max_length=50)

Creating a serializer

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

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

Creating a view set

Override get_serializer with the field value set in the query string only on GET requests Dynamically change the columns to 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)

Operation check

If no field is specified

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"
  }
]

When code, name is specified in the field

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"
  }
]

Recommended Posts

django-rest-framework Dynamically change serializer fields
Dynamically add form fields in Django