DRF ManyToMany Field getting an error when creating object

Question:

I have a Rant model with Category linked to it using ManyToManyField. I’ve serialized it but the problem is this error:

{
  "categories": [
    "Expected a list of items but got type "str"."
  ]
}

These are my serializers:

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = "__all__"


class RantSerializer(serializers.ModelSerializer):
    categories = CategorySerializer(many=True)


    class Meta:
        model = Rant
        fields = ('rant', 'slug', 'categories')

My post method is this:

    def post(self, request, format=None):
        serializer = RantSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

What did I do wrong here?

Answers:

Currently you might be send a request with body something like this,

{
    "rant": "This is my rant",
    "slug": "my-rant",
    "categories": 1
}

Since categories is a ManyToMany relation, it is expecting a list of ids,

Give this a try:

{
    "rant": "This is my rant",
    "slug": "my-rant",
    "categories": [1, 2, 3]
}
Answered By: Sumithran

The problem is with serializer, and your serializer is read-only, and threrefore serializer.save() has no effect.

In order to change this behavior, you need to overwrite serializer methods: create() and update(), or/and save().

Check this documentation for implementation details: https://www.django-rest-framework.org/api-guide/serializers/#saving-instances

Answered By: alv2017

I had faced a similar problem. Do the following to solve your issue

  1. Install the drf-writable-nested with pip install drf-writable-nested
  2. Re-write your serializers like so
# --- snip ---
from drf_writable_nested.serializers import WritableNestedModelSerializer

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = "__all__"


class RantSerializer(WritableNestedModelSerializer):
    categories = CategorySerializer(many=True)


    class Meta:
        model = Rant
        fields = ('rant', 'slug', 'categories')
# ---snip ----

The catch here is to import and implement the WritableNestedModelSerializer class. Also note that the serializers.ModelSerializer super class has been replaced on the serializer where you want to do the nested payload.

  1. Trying performing the API calls. You should be good to go.

For more information, refer to this repo’s README here

Answered By: Chilusoft