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

?

+3


source to share


2 answers


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)

      

+4


source


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)

      

0


source







All Articles