Change a field in a Django REST Framework ModelSerializer based on the request type?

There is a feature of DRF where you can dynamically change the fields on the serializer http://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields

My use case: use slug field on GET so we can see nice rep of a relation, but on POST/PUT switch back to the classic primary key update. Adjust your serializer to something like this:

class FooSerializer(serializers.ModelSerializer):
    bar = serializers.SlugRelatedField(slug_field='baz', queryset=models.Bar.objects.all())

    class Meta:
        model = models.Foo
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super(FooSerializer, self).__init__(*args, **kwargs)

        try:
            if self.context['request'].method in ['POST', 'PUT']:
                self.fields['bar'] = serializers.PrimaryKeyRelatedField(queryset=models.Bar.objects.all())
        except KeyError:
            pass

The KeyError is sometimes thrown on code initialisation without a request, possibly unit tests.

Enjoy and use responsibly.


IMHO, multiple serializers are only going to create more and more confusion.

Rather I would prefer below solution:

  1. Don't change your viewset (leave it default)
  2. Add .validate() method in your serializer; along with other required .create or .update() etc. Here, real logic will go in validate() method. Where based on request type we will be creating validated_data dict as required by our serializer.

I think this is the cleanest approach.

See my similar problem and solution at DRF: Allow all fields in GET request but restrict POST to just one field


You are looking for the get_serializer_class method on the ViewSet. This allows you to switch on request type for which serializer that you want to use.

from rest_framework import viewsets

class MyModelViewSet(viewsets.ModelViewSet):

    model = MyModel
    queryset = MyModel.objects.all()

    def get_serializer_class(self):
        if self.action in ('create', 'update', 'partial_update'):
            return MySerializerWithPrimaryKeysForCreatingOrUpdating
        else:
            return MySerializerWithNestedData