How to allow the update of only 1 field?

Viewed 477

So Im doing an app where the user can upload photos, each photo has a user, image, description, and other fields. When the user creates a new photo, the image is required, but when the user wants to update it, you only can change the description. I'm stuck with this, first tried with update (put) but it's clearly not the method to use, now I'm trying with partial_update but I can't get it right.

This is what I have right now, it gives me the error: TypeError: Object of type Photo is not JSON serializable.

Maybe it's a completely wrong way of doing it.

View:

class PhotoViewSet(mixins.RetrieveModelMixin,
               mixins.CreateModelMixin,
               mixins.UpdateModelMixin,
               mixins.DestroyModelMixin,
               viewsets.GenericViewSet):
""" PhotoViewSet

Handle upload (create) and delete of photos.
"""

queryset = Photo.objects.all()
serializer_class = PhotoModelSerializer

def perfom_create(self, serializer):
    """ Upload a new photo. """
    serializer.save()

def perform_destroy(self, instance):
    """ Delete a photo. """
    return super().perform_destroy(instance)

def retrieve(self, request, *args, **kwargs):
    """ Retrieve photo information. """
    response = super(PhotoViewSet, self).retrieve(request, *args, **kwargs)
    data = {
        'photo': response.data,
    }
    response.data = data
    return response

def partial_update(self, request, pk=None):
    serializer = UpdateDescriptionSerializer(data=request.data, partial=True)
    serializer.is_valid(raise_exception=True)
    data = serializer.save()
    return Response(data=data, status=status.HTTP_202_ACCEPTED)

serializer:

class UpdateDescriptionSerializer(serializers.ModelSerializer):
""" Update description serializer. """

description = serializers.CharField(max_length=255)

class Meta:
    """ Meta class. """

    model = Photo
    fields = ('description',)
2 Answers

You should pass the instance that you wish to update. Furthermore you should not return the outcome of the .save() method, but the data of the serializer, so:

def partial_update(self, request, pk=None):
    instance = self.get_object()
    serializer = UpdateDescriptionSerializer(
        instance,
        data=request.data,
        partial=True
    )
    serializer.is_valid(raise_exception=True)
    serializer.save()
    return Response(data=serializer.data, status=status.HTTP_202_ACCEPTED)

If you want to return all the details of the updated item, you can work with another serializer that will create the data for the response, so:

def partial_update(self, request, pk=None):
    instance = self.get_object()
    serializer = UpdateDescriptionSerializer(
        instance,
        data=request.data,
        partial=True
    )
    serializer.is_valid(raise_exception=True)
    serializer.save()
    serializer2 = PhotoModelSerializer(instance)
    return Response(data=serializer2.data, status=status.HTTP_202_ACCEPTED)

You should use 2 different serializers, of which 1 of them is dedicated to update your object e.g. PhotoSerializer and UpdatePhotoSerializer. What I would suggest is to override the get_serializer_class() method as follows:

def get_serializer_class(self):
    if self.request.method == "PATCH":  # add PUT too if you wish
        return UpdatePhotoSerializer

    return PhotoSerializer

Another tip, using generic views is much cleaner in your views as per documentation.

Related