Reputation: 625
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
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
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