Skip to content
Advertisement

How can I fix my REST view to PATCH and serialize updates to an ArrayField() in my model?

I have a view that is used to update a field in my model. It’s represented as follows: stock_list = ArrayField(models.CharField())

Each value in the ArrayField is separated by commas.

I used a custom serializer method to allow for my backend to separate the elements in my PATCH obj by commas.

  • serializers.py:
class StringArrayField(ListField):
    """
    String representation of an array field.
    """
    def to_representation(self, obj):
        obj = super().to_representation(obj)
        # convert list to string
        return ",".join([str(element) for element in obj])

    def to_internal_value(self, data):
        data = data.split(",")  # convert string to list
        return super().to_internal_value(self, data)


class StockListSerializer(serializers.ModelSerializer):
    stock_list = StringArrayField()

    class Meta:
        model = Bucket
        fields = ("stock_list",)

Below is my view that I use, the URL’s are linked up correctly, however I’m setting up my view wrong:

  • view.py:
class EditBucketSymbols(generics.RetrieveUpdateAPIView):
    permission_classes = [IsAuthenticated]
    serializer_class = StockListSerializer

    def get_queryset(self):
        return Bucket.objects.all()

    def get_object(self, queryset=None, **kwargs):
        item = self.kwargs.get('pk')
        return get_object_or_404(Bucket, pk=item)

    def patch(self, request, *args, **kwargs):
        item = BucketDetail.get_object(self)
        data = request.data
        item.stock_list = data.get("stock_list", item.stock_list)
        serializer = StockListSerializer(data=item.stock_list,  partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Here is the PATCH error I get:

{
    "non_field_errors": [
        "Invalid data. Expected a dictionary, but got str."
    ]
}

I’m not sure why my view is expecting a dictionary, I only want the users to PATCH the arrayfield known as stock_list with the data they input.

Appreciate any debugging assistance here to get my PATCH view working properly as expected.

Advertisement

Answer

First, you need to change the super() method calling of to_internal_value(...) method (you were not calling it correctly)

class StringArrayField(serializers.ListField):
    def to_representation(self, obj):
        obj = super().to_representation(obj)
        return ",".join([str(element) for element in obj])

    def to_internal_value(self, data):
        data = data.split(",")
        return super().to_internal_value(data) # update here

and then, use the generics.RetrieveUpdateAPIView “as-is”, because, you don’t need any alterations to the view (at-least the minimal case you have given in the OP)

So, your view will become,

class EditBucketSymbols(generics.RetrieveUpdateAPIView):
    permission_classes = [IsAuthenticated]
    serializer_class = StockListSerializer
    queryset = Bucket.objects.all()

This will let you update the data with ease and keep in mind that the DRF expect the data in the following format,

{
    "stock_list":"this,is,patch,request,test"
}

Example Result ScreenShot Example Result ScreenShot

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement