Reputation: 2378
I can't get DRF to allow providing a read_only version of nested data and a writeable version of just the ids in a list in one serializer. It feels like a bug to me, but usually that just means I'm not understanding the framework well enough, and being misled by the error message.
class Individual(models.Model):
household = models.ForeignKey(
'household.Household',
null=True,
related_name="individuals")
name = models.CharField(
max_length=100, default='')
class Household(models.Model):
address_line1 = models.CharField(max_length=64, default='')
class IndividualListSerializer(serializers.ModelSerializer):
class Meta:
model = Individual
depth = 0
fields = ('url', 'id', 'name', 'household')
read_only_fields = fields
class HouseholdUpdateSerializer(serializers.ModelSerializer):
individuals_details = IndividualListSerializer(many=True, source='individuals', read_only=True)
class Meta:
model = Household
fields = ('id', 'address_line1', 'individuals', 'individuals_details')
read_only_fields = ('id', 'individuals_details')
The error comes back as
AssertionError: The `.update()` method does not support writable nested fields by default. Write an explicit `.update()` method for serializer `household.serializers.HouseholdUpdateSerializer`, or set `read_only=True` on nested serializer fields. // Werkzeug Debugger</title>
I have used read_only on the nested field (which is needed in the response to the update). Yet the error still indicates I am not doing so.
If I remove the individuals
field entirely, the individuals_details
returns the readable data without the error, but since it's ignoring the individuals
data being sent, it isn't updating that list.
If I remove the individuals_details
field, DRF accepts the individuals
list and performs the update on the model. But then the return data needed is not there.
So, while either the read nested or write list work on their own, when the other is added the serializer doesn't function.
This seems a pretty common area people get stuck, and it seems the answers this SO question have become the best practice for a pattern. But it doesn't work in my code for some reason. Perhaps because of the ManyToOne in my models.
I can probably work around this by changing the client to perform the PUT for the update, ignore the response, and then do a separate GET, but that's sloppy and shouldn't be needed if the DRF update can be made to work as expected.
What am I missing in this?
Upvotes: 1
Views: 3586
Reputation: 8897
You ask several questions so I'll start with the representation. Actually you don't need separate field for full Individual
view you could just ovveride to_representation
method.
class HouseholdUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Household
fields = ('id', 'address_line1', 'individuals')
def to_representation(self, instance):
representation = super(HouseholdUpdateSerializer, self).to_representation(instance)
representation['individuals'] = IndividualListSerializer(instance.individuals.all(), many=True).data
return representation
Usually when you want update related fields of your model you should override create
and update
serializer methods.
class HouseholdUpdateSerializer(serializers.ModelSerializer):
....
def update(instance, validated_data):
# get and remove individuals from validated_data
individuals = validated_data.pop('individuals')
# delete all related links to individuals
# You could provide some validation before clear, check if provided pks exists in db table
instance.individuals.clear()
# update related links with new individuals
instance.individuals.add(*individuals)
# call super to provide update for other fields
return super(HouseholdUpdateSerializer, self).update(validated_data)
create
will probably works well in your case without overriding. If it not just write it similar to update
.
Upvotes: 1