samad montazeri
samad montazeri

Reputation: 1273

django rest framework: limit fields that can be updated

I want users to be able to update only one specific field. For example:

models.py

class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)

    class Meta:
        ordering = ('created',)

serializer.py

class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style')

views.py

class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

Once the Snippet is created, the user should only be able to update title field.

I know I can achieve that by something like this:

serializers.py

def update(self, instance, validated_data):
        """
        Update and return an existing `Snippet` instance, given the validated data.
        """
        instance.title = validated_data.get('title', instance.title)
        instance.save()
        return instance

In serializer class. But I want to know, is there a way that browsable API show only title field in edit form? And also skip validation for fields that are not required?

Upvotes: 25

Views: 9430

Answers (2)

Art
Art

Reputation: 3089

While @petkostas answer is correct, it doesn't give you a full picture of how to achieve it.

First, Create a new serializer; let's call it SnippetUpdateSerializer

Now, you may have custom serializer fields like serializers.MethodFieldSerializer that you would have defined in SnipperSerializer; which you may not want to write again in your new serializer. A good approach is to use inheritance.

Taking the example from the question

class SnippetUpdateSerializer(SnippetSerializer): #<- pay attention here 

    class Meta(SnippetSerializer.Meta): # <- pay attention here 
    
        SnippetSerializer.Meta.extra_kwargs.update({ # update the dictionary
            'id': {'read_only': True},
            'code': {'read_only': True}, # you can also use {write_only: True} if you want this to be write only
            'lineos': {'read_only': True},
            'language': {'read_only': True},
            'style': {'read_only': True}
        }) # you may completely override  by just using extra_kwargs, instead of SnippetSerializer.Meta.extra_kwargs

Now in your SnippetUpdateView, use the above serializer.

If you are using class based views then set serializer_class = SnippetUpdateSerializer

Another approach is to return bad request, from your update view if the user requests contain read_only fields. (not recommended)

Upvotes: 0

petkostas
petkostas

Reputation: 7460

Django REST Framework provides the read_only and write_only attributes for controlling what is used for editing and what is not.

serializers.py

class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
        extra_kwargs = {
            'id': {'read_only': True},
            'code': {'read_only': True},
            'lineos': {'read_only': True},
            'language': {'read_only': True},
            'style': {'read_only': True}
        }

The above will return all the fields on read requests but only title will be writable. You can find more at the official documentation: http://www.django-rest-framework.org/api-guide/serializers/#specifying-read-only-fields

Upvotes: 13

Related Questions