Alex
Alex

Reputation: 625

django rest framework update nested serializer by id

How to update a list in a nested serializer by id

I have a user who has multiple contacts

Example:

contact serializer

class ContactSerializer(serializers.ModelSerializer):
    class Meta:
        model = Contact
        fields = [
            'id',
            'name',
            'last_name'
        ]

user serializer

class UserSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(
        required=True,
        validators=[
            UniqueValidator(queryset=User.objects.all())
        ]
    )
    contacts = ContactSerializer(many=True)

    class Meta:
        model = User
        fields = [
            "email",
            "contacts"
        ]


    def update(self, instance, validated_data):
        contacts_data = validated_data.pop('contacts')
        contacts = (instance.contacts).all()
        contacts = list(contacts)
        instance.name = validated_data.get('name', instance.name)
        instance.save()

        # many contacts
        for contact_data in contacts_data:
            contact = contacts.pop(0)
            contact.name = contact_data.get('name', contact.name)
            contact.last_name = contact_data.get('last_name', contact.last_name)
            contact.save()

        return instance

I want to update contacts by ID, for now it works, but it only updates the first contact in the list

Example I want to update the contact with ID 4 and 6

Payload request

{
    "name": "string",
    "contacts": [
        {
            "id": 4, 
            "name": "string",
            "last_name": "string"
        },
        {
            "id": 6, 
            "name": "string",
            "last_name": "string"
        }
    ]
}

Any ideas or recommendations?

Upvotes: 2

Views: 7364

Answers (2)

foranys
foranys

Reputation: 11

In case anyone else has the same problem with the above mentioned solution: DRF seems to remove the "id" field from validated_data if this field is not a writable field. Setting it writable does not make sense in my eyes as we only want it to get the correct objects.

EDIT: the proper way to do this, is probably to add the id field to the ContactSerializer as follows (credit goes to https://stackoverflow.com/a/37275096):

id = serializers.IntegerField(required=False)

OLD: Here is my workaround, tho I am happy to read the proper way to do this:

Overwrite the to_internal_value of your ContactSerializer as follows:

def to_internal_value(self, data):
    id_field = data.get("id", None)
    ret = super().to_internal_value(data)
    ret["id"] = id_field
    return ret

Upvotes: 0

Jimmar
Jimmar

Reputation: 4459

Try it like this

def update(self, instance, validated_data):
        contacts_data = validated_data.pop('contacts')

        instance.name = validated_data.get('name', instance.name)
        instance.save()

        # many contacts
        for contact_data in contacts_data:
            contact = Contact.objects.get(pk=contact_data['id']) # this will crash if the id is invalid though
            contact.name = contact_data.get('name', contact.name)
            contact.last_name = contact_data.get('last_name', contact.last_name)
            contact.save()

        return instance

Upvotes: 8

Related Questions