How to deserialize nested objects with Django Rest Framework
Let's say I have Django models like this:
class Book(models.Model):
title = models.CharField(max_length=150)
author = models.CharField(max_length=150)
class Chapter(models.Model):
book = models.ForeignKey(Book, related_name='chapters')
title = models.CharField(max_length=150)
page_num = models.IntegerField()
and the Django Rest Framework classes:
class ChapterSerializer(serializers.ModelSerializer):
class Meta:
model = Chapter
fields = ('id', 'title', 'page_num')
class BookSerializer(serializers.ModelSerializer):
chapters = ChapterSerializer(many=True)
class Meta:
model = Book
fields = ('id', 'title', 'author', 'chapters')
def create(validated_data):
chapters = validated_data.pop('chapters')
book = Book(**validated_data)
book.save()
serializer = ChapterSerializer(data=chapters, many=True)
if serializer.is_valid(raise_exception=True):
chapters = serializer.save()
class BookCreate(generics.CreateAPIView):
serializer = BookSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
# Do some other stuff
and I will send some JSON like this:
{
title: "Test book",
author: "Test author",
chapters: [
{title: "Test chapter 1", page_num: 1},
{title: "Test chapter 2", page_num: 5}
]
}
I am getting an exception because it chapter
doesn't have one associated with it book
. If I add book
to one of the fields ChapterSerializer
, then JSON won't validate because BookSerializer
in BookCreate
won't validate because it expects a book id for chapters, but the book hasn't been created yet. How can this situation be resolved?
Is there a way to have BookSerializer
my own fields validated and not validated chapter
?
source to share
You can pass additional arguments to .save
. So I think you just need to pass your newly created book instance to serializer
, for example
def create(validated_data):
chapters = validated_data.pop('chapters')
book = Book(**validated_data)
book.save()
serializer = ChapterSerializer(data=chapters, many=True)
if serializer.is_valid(raise_exception=True):
chapters = serializer.save(book=book)
source to share
I think you shouldn't create another Serializer inside a method create()
for the serializer because it is redundant. Validation has already been done by the serializer if you defined it as the serializer for the model referenced by this field:
class BookSerializer(serializers.ModelSerializer):
# here you define the serializer to validate your input
chapters = ChapterSerializer(many=True)
instead you can just create an object, the data has already been validated by calling the is_valid()
original serializer. you still need to pass the book to the method create()
:
def create(validated_data):
chapters_data = validated_data.pop('chapters')
book = Book.objects.create(**validated_data)
for chapter_data in chapters_data:
Chapter.objects.create(book=book, **chapter_data)
source to share