Home > Software engineering >  postman only showing "This field is required" to ManyToMany field in django
postman only showing "This field is required" to ManyToMany field in django

Time:10-09

I have 2 models - Module and Room. A module can have zero or multiple rooms and a room can be added into multiple modules. So, there is a simple many-to-many relationship between them.

When I use post request, raw-data works, but not form-data.

module/models.py -

class Module(models.Model):
    module_id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=100)
    desc = models.TextField()
    room_list = models.CharField(max_length = 100, blank=True)
    rooms = models.ManyToManyField(Rooms, blank=True)

rooms/models.py -

class Rooms(models.Model):
    room_id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=100)
    level = models.CharField(max_length=100)
    desc = models.TextField()

module/serializers.py -

class ModuleSerializer(serializers.ModelSerializer):
    rooms = RoomSerializer(many=True)

    class Meta:
        model = Module
        fields = '__all__'

    def create(self, validated_data):
        rooms_data = validated_data.pop('rooms')
        module = Module.objects.create(**validated_data)
        for data in rooms_data:
            room = Rooms.objects.get(**data)
            module.rooms.add(room)
        return module

    def update(self, instance, validated_data):

        # Updating rooms
        rooms_data = validated_data.get('rooms')
        instance.rooms.clear()
        
        for room_data in rooms_data:
            room = Rooms.objects.get(**room_data)
            instance.rooms.add(room)
        
        # Updating other fields
        fields = [
            'title',
            'desc',
            'thumbnail',
            'is_deleted',
        ]
        for field in fields:
            setattr(instance, field, validated_data[field])
        instance.save()
        return instance

rooms/serialier.py -

class RoomSerialize(serializers.ModelSerializer):
    room_id = serializers.IntegerField()
    class Meta:
        model = Rooms
        fields = "__all__"

module/views.py -

class add_module(APIView):
def post(self, request, format=None):

    # Adding the rooms to module from room_list
    new_request = request.data.copy()
    room_list=[]
    if 'room_list' in new_request:
        room_list_data = list(new_request['room_list'].split(" "))
        for room in room_list_data:
            room_object = Rooms.objects.get(room_id=room)
            room_serializer = RoomSerializer(room_object)
            room_list.append(room_serializer.data)
    new_request.update({'rooms':room_list})

    # creating the module
    module_serializer = ModuleSerializer(data=new_request)
    if module_serializer.is_valid():
        module_serializer.save()
        return Response(module_serializer.data['module_id'])
    return Response(module_serializer.errors)

POST request body for updating a module in POSTMAN -

{
    "module_id": 2,
    "room_list": "1 2",
    "title": "4",
    "desc": "22",
}

Pls notice that while taking input of ManyToMany field - "rooms", I'm taking a string "room_list" as input that contains all the room_ids to be included. This works perfectly fine when I take input as raw-data in postman, but when I use form-data, it shows

{
    "rooms": [
        "This field is required."
    ]
}

What to do?

CodePudding user response:

First of all, serializer fields with (many=True) create nested objects. If you input data with Rooms as already serialized, then it means that you create other Rooms instances. It will be shown with if module_serializer.is_valid(): part. Therefore, if you intended to implement as just link already instantiated Rooms to Module, it's better to make it as primary key list.

Here are my example codes.

class ModuleSerializer(serializers.ModelSerializer):
    rooms = RoomSerializer(many=True, read_only=True)

    def create(self, validated_data):
        rooms_data = validated_data['room_list']
        module = Module.objects.create(**validated_data)
        for data in rooms_data.split(' '):
            room = Rooms.objects.get(room_id=data)
            module.rooms.add(room)
        return module

    def update(self, instance, validated_data):

        # Updating rooms
        rooms_data = validated_data.get('rooms')
        instance.rooms.clear()

        for room_data in rooms_data:
            room = Rooms.objects.get(**room_data)
            instance.rooms.add(room)

        # Updating other fields
        fields = [
            'title',
            'desc',
            'thumbnail',
            'is_deleted',
        ]
        for field in fields:
            setattr(instance, field, validated_data[field])
        instance.save()
        return instance

    class Meta:
        model = Module
        fields = '__all__'
class AddModule(APIView):
    def post(self, request, format=None):
        # creating the module
        module_serializer = ModuleSerializer(data=request.data)
        if module_serializer.is_valid():
            module_serializer.save()
            return Response(module_serializer.data['module_id'])
        return Response(module_serializer.errors)

Any other parts are same as yours. Regardless of whether data are sent by form-data or json raw data, results are same as below.

enter image description here

enter image description here

  • Related