11👍
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
2👍
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',
)
- [Django]-Adding django admin permissions in a migration: Permission matching query does not exist
- [Django]-Python Asyncio in Django View
- [Django]-Detect mobile, tablet or Desktop on Django
1👍
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.
- [Django]-Django index page best/most common practice
- [Django]-Django TypeError: get() got multiple values for keyword argument 'invoice_id'
- [Django]-Whats the difference between using {{STATIC_URL}} and {% static %}
0👍
Django is a little bit annoying that it ignores relationship fields if depth = 1
One explicit way to deal with this is to define 2 serializers, and define which to use in your Viewset
views:
class MyViewSet(viewsets.ModelViewSet):
queryset = models.MyModel.objects.order_by("pk")
serializer_class = serializers.ExpandedSerializer
def get_serializer_class(self):
if self.action == ["list", "retrieve"]:
return serializers.MyExpandedSerializer
if self.action in ["create", "update", "partial_update"]:
return serializers.MyCreateSerializer
return super(LenderOfferingViewSet, self).get_serializer_class()
def create(self, request, *args, **kwargs):
"""Overload the create method with a different response serializer."""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
response_serializer = serializers.MyExpandedSerializer(serializer.instance)
return Response(response_serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def update(self, request, *args, **kwargs):
"""Overload method for PUT/PATCH to correctly set values and set response serializer."""
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
response_serializer = serializers.MyExpandedSerializer(serializer.instance)
return Response(response_serializer.data)
And then serializers:
class MyCreateSerializer(serializers.ModelSerializer):
class Meta:
model = models.MyObject
fields = ["issue_name", "status"]
depth = 0 # no expansion else create/update fail on missing fields (serializer ignores related fields)
class MyExpandedSerializer(serializers.ModelSerializer):
"""This serializer expands related fields explicitly with SerializerMethodFields."""
class Meta:
model = models.My
fields = ["id", "issue_name", "status"]
depth = 1
Then, when you
POST /my_endpoint/
{
issue_name: 'This is a problem',
status: 3
}
You will get the response (on i.e. /my_endpoint/1)
{
id: 1,
issue_name: 'This is a problem',
status: {
id: 3,
name: 'todo'
}
}
- [Django]-How do you configure Django to send mail through Postfix?
- [Django]-How do Django models work?
- [Django]-Homepage login form Django