I'm facing a little problem right now with Django Rest Framework. I'm trying to post an object with nested objects in it.
Here are my serializers.py
:
class ClassSerializer(serializers.ModelSerializer):
class Meta:
model = Class
fields = ('number', 'letter')
class SubjectSerializer(serializers.ModelSerializer):
class Meta:
model = Subject
fields = ('title',)
class ExamSerializer(serializers.ModelSerializer):
subject = SubjectSerializer()
clazz = ClassSerializer()
class Meta:
model = Exam
fields = ('id', 'subject', 'clazz', 'topic', 'date', 'details')
depth = 1
def create(self, validated_data):
return Exam.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.__dict__.update(**validated_data)
instance.save()
return instance
And create()
from views.py
:
def create(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
return Response(serializer.validated_data, status=status.HTTP_201_CREATED)
And here it is the response from Postman:
I've read some posts here about this problem but I'm still stuck with it. I've tried to fix it in several ways but it is still returning "This field is required."
.
You are dealing with the problem of nested serialization. Please read the linked documentation before proceeding.
Your question relates to a complex area of problems in DRF and hence requires some explanation and discussion for understanding how serializers and viewsets work.
I will discuss the problem of representing your
Subject
andClass
data via the same endpoint by using a different representation of data for different HTTP methods, because this is commonly the problem when people wish to represent their data in nested formats; they wish to provide their user interfaces enough information for clean use, e.g. through the dropdown selectors.By default Django and Django REST Framework (DRF) refer to related objects (your
Subject
andClass
) by their primary keys. These, by default, are auto-incrementing integer keys with Django. If you want to refer to them by other ways you have to write overrides for this. There are a few different options.Class
models up with a composite search that consists of a composite (number, letter) search term). You can override related object lookups in yourcreate
view method (for POST), for example, but then you will have to handle similar lookups in yourupdate
view method as well (for PUT and PATCH).Option 1: Look Class and Subject up with an arbitrary attribute in create and update:
Set your nested class serializers as read-only:
Override your view's create to look up the related classes on free-form attributes. Also, check out how DRF implements this with mixins. You will also have to override your
update
method to handle these correctly, and take into accountPATCH
(partial update) support in addition toPUT
(update) if you take this route:Option 2: Specialize your serializers for read and write and use primary keys; This is the idiomatic approach:
First define a default ModelSerializer you wish to use for normal operations (POST, PUT, PATCH):
Then override the necessary fields with the kind of representation you want to give to them for reading the data (GET):
Then specify the serializer you wish to use for different operations for your ViewSet. Here we return the nested Subject and Class data for read operations, but only use their primary keys for update operations (far simpler):
As you can see, option 2 seems fairly less complex and error-prone, containing only 3 lines of hand-written code on top of DRF (the get_serializer_class implementation). Just let the framework's logic figure out the representations and creation and updates of objects for you.
I have seen many other approaches, but so far these have been the ones that have produced the least code to maintain for me and take advantage of the design of DRF in a clean manner.
I had the same issue when trying to post a nested JSON object to DRF (Django Rest Framework).
Once you've properly set up writing nested serializers (see the docs on writable nested serializers), you can test that it works by using the browsable API and posting/putting data there. If that works, and you're still getting "This field is required" errors on your nested models when posting/putting JSON objects, you may have to set the content-type of your request.
This answer provided the solution I needed, and it's summarised below.
I needed to set the "contentType" as well as "stringify" my js object.
An easier approach without doing any additional classes is to take serialization on yourself: