djangoserializationdjango-rest-frameworkm2m

List Field serializer giving 'ManyRelatedManager' object is not iterable error with M2M field


My models.py looks like this:

class IP(models.Model):
    name = models.CharField(max_length=30, unique=True)
    address = models.CharField(max_length=50, unique=True)

class IPGroup(models.Model):
    name = models.CharField(max_length=50, unique=True)
    ips = models.ManyToManyField('IP', through='IPGroupToIP')

class IPGroupToIP(BaseModel):
    ip_group = models.ForeignKey('IPGroup', on_delete=models.CASCADE)
    ip = models.ForeignKey('IP', on_delete=models.CASCADE)

Now, in order to create an IPGroup, I have the following serializer:

class IPGroupCreateSerializer(serializers.ModelSerializer):
    ips = serializers.ListField()

class Meta:
    model = IPGroup
    fields = ['name', 'ips']

@transaction.atomic()
def create(self, validated_data):
    ips_data = validated_data.pop('ips', None)
    ip_group = IPGroup.objects.create(name=validated_data['name'])

    if ips_data:
        for ip in ips_data:
            ip_obj, created = IP.objects.get_or_create(name=ip['name'], address=ip['address'])
            IPGroupToIP.objects.create(ip_group_id=ip_group.id, ip_id=ip_obj.id)
    return ip_group

My views are a simple class based view as follows:

class IPGroupCreateView(generics.CreateAPIView):
    queryset = IPGroup.objects.get_queryset()
    serializer_class = IPGroupCreateSerializer

My JSON payload looks like this:

{ "ips": [{"name":"example.com", "address":"10.1.1.9"}], "name": "ipgroup1" }

This how ever gives me an error stating:

TypeError at /api/v1/ip-group/
'ManyRelatedManager' object is not iterable

The strange thing is that when i check the DB, the IPGroup is created along with the M2M ips. So, the code is working as expected but the view is somehow returning a 500 server error instead of a 201 created. What am i doing wrong ?


Solution

  • Due to some complications, ListField() will become handy only while writing to the DB ( I'm not sure why this behavior).

    In your context, adding write_only=True in ListField solve the exception. Hence the IPGroupCreateSerializer will be as

    class IPGroupCreateSerializer(serializers.ModelSerializer):
        ips = serializers.ListField(write_only=True)



    I personally reccomend to use Nested Serializers to handle this situation. So,define a new IPSerializer class and use it instead of serializers.ListField()

    class IPSerializer(serializers.ModelSerializer):
        class Meta:
            model = IP
            fields = ('name', 'address')
    
    
    class IPGroupCreateSerializer(serializers.ModelSerializer):
        ips = IPSerializer(many=True)
    
    
    class Meta:
        model = IPGroup
        fields = ['name', 'ips']