7

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 Django Rest Framework classes like this:

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 POST 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 get an exception because chapter doesn't have a book associated with it. If I add book as one of the fields of ChapterSerializer, then the JSON will fail to validate because the BookSerializer in BookCreate won't validate because it will expect a book id for the chapters, but the book hasn't been created yet. How do I resolve this situation?

Is there a way to have the BookSerializer to validate its own fields and not to validate its chapter's?

2 Answers 2

7

You can pass additional arguments on .save. So I think you just need to pass the newly created book instance to the serializer, e.g.

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)
Sign up to request clarification or add additional context in comments.

2 Comments

Wow, thank you! I can't believe how much time I wasted with that.
@trixn made a good point regarding redundant validation inside the serializer, check his answer.
3

I think, you should not create another Serializer inside the create()method of a serializer because this is redundant. The validation has already been done by the serializer if you defined it to be 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 the object, the data has already been validated by the call to is_valid() of the initial serializer. you need to pass the book to the create()method anyways:

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)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.