Django-Rest-Framework. Updating nested object
I came across the same problem recently. The way I addressed it was to force the id
to be a required field:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('id', 'name', 'url', )
extra_kwargs = {'id': {'read_only': False, 'required': True}}
This way I was able to retrieve the correct instance and update it
This is the way I've accomplished the task:
I've added an id
field to the InvoiceItemSerializer
class InvoiceItemSerializer(serializers.ModelSerializer):
...
id = serializers.IntegerField(required=False)
...
And the update method for the InvoiceSerializer
def update(self, instance, validated_data):
instance.nr = validated_data.get('nr', instance.nr)
instance.title = validated_data.get('title', instance.title)
instance.save()
items = validated_data.get('items')
for item in items:
item_id = item.get('id', None)
if item_id:
inv_item = InvoiceItem.objects.get(id=item_id, invoice=instance)
inv_item.name = item.get('name', inv_item.name)
inv_item.price = item.get('price', inv_item.price)
inv_item.save()
else:
InvoiceItem.objects.create(account=instance, **item)
return instance
Also in the create
method I'm popping the id
if it is passed.
All of these solutions seemed too complex or too specific for me, I ended up using code from the tutorial here which was incredibly simple and reusable:
from rest_framework import serializers
from django.contrib.auth import get_user_model
from myapp.models import UserProfile
# You should already have this somewhere
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ['nested', 'fields', 'you', 'can', 'edit']
class UserSerializer(serializers.ModelSerializer):
# CHANGE "userprofile" here to match your one-to-one field name
userprofile = UserProfileSerializer()
def update(self, instance, validated_data):
# CHANGE "userprofile" here to match your one-to-one field name
if 'userprofile' in validated_data:
nested_serializer = self.fields['userprofile']
nested_instance = instance.userprofile
nested_data = validated_data.pop('userprofile')
# Runs the update on whatever serializer the nested data belongs to
nested_serializer.update(nested_instance, nested_data)
# Runs the original parent update(), since the nested fields were
# "popped" out of the data
return super(UserSerializer, self).update(instance, validated_data)
EDIT: Bugfix, I added a check for the nested field's existence before attempting to update it.