I am a bit frustrated with this problem using the Django Rest Framework:
I am using a viewset, with a custom serializer
. This serializer has its depth set to 1
. When i query this viewset I get the correct representation of data for example:
data = {
id: 1,
issue_name: 'This is a problem',
status: {
id: 3,
name: 'todo'
}
}
The problem comes in when I need to update the status. For example if I want to select another status for this issue, for example:
status_new = {
id: 4,
name: 'done'
}
I send the following PATCH back to the server, this is the output:
data = {
id: 1,
issue_name: 'This is a problem',
status: {
id: 4,
name: 'done'
}
}
However, the status does not get updated. Infact, it is not even a part of the validated_data dictionary. I have read that nested relations are read-only. Could someone please tell me what I need to do this in a simple way?
Would really be obliged.
Thanks in advance
As stated in the documentation, you will need to write your own create()
and update()
methods in your serializer to support writable nested data.
You will also need to explicitly add the status field instead of using the depth
argument otherwise I believe it won't be automatically added to validated_data
.
EDIT: Maybe I was a bit short on the details: what you want to do is override update in ModelIssueSerializer. This will basically intercept the PATCH/PUT requests on the serializer level. Then get the new status and assign it to the instance like this:
class StatusSerializer(serializers.ModelSerializer):
class Meta:
model = Status
class ModelIssueSerializer(serializers.ModelSerializer):
status = StatusSerializer()
# ...
def update(self, instance, validated_data):
status = validated_data.pop('status')
instance.status_id = status.id
# ... plus any other fields you may want to update
return instance
The reason I mentioned in the comment that you might need to add a StatusSerializer
field is for getting status into validated_data
. If I remember correctly, if you only use depth
then nested objects might not get serialized inside the update()
/ create()
methods (although I might be mistaken on that). In any case, adding the StatusSerializer
field is just the explicit form of using depth=1
I usually use custom field for such cases.
class StatusField(serializers.Field):
def to_representation(self, value):
return StatusSerializer(value).data
def to_internal_value(self, data):
try:
return Status.objects.filter(id=data['id']).first()
except (AttributeError, KeyError):
pass
And then in main serializer:
class IssueSerializer(serializers.ModelSerializer):
status = StatusField()
class Meta:
model = MyIssueModel
fields = (
'issue_name',
'status',
)
I would assume that your models mimic your serializer's data. Also, I would assume that you have a one to many relation with the status(es) but you don't need to create them via the issue serializer, you have a different endpoint for that. In such a case, you might get away with a SlugRelatedField.
from rest_framework import serializers
class StatusSerializer(serializers.ModelSerializer):
class Meta:
model = MyStatusModel
fields = (
'id',
'status',
)
class IssueSerializer(serializers.ModelSerializer):
status = serializers.SlugRelatedField(slug_field='status', queryset=MyStatusModel.objects.all())
class Meta:
model = MyIssueModel
fields = (
'issue_name',
'status',
)
Another valid solution would be to leave here the foreign key value and deal with the display name on the front-end, via a ui-select or select2 component - the RESTfull approach: you are handling Issue objects which have references to Status objects. In an Angular front-end app, you would query all the statuses from the back-end on a specific route and then you will display the proper descriptive name based on the foreign key value form Issue.
Let me know how is this working out for you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With