Drf Writable nested serializers "This field is required."

Here is the code: Models.py

class Question(models.Model):
    lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE, related_name='questions')
    question = models.CharField('lesson name', max_length=120)


class Option(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='options')
    option = models.CharField('test option', max_length=100)
    correct = models.BooleanField(default=False)

Views.py

class QuestionAPIView(generics.CreateAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer

Serializers.py

class OptionSerializer(serializers.ModelSerializer):

    class Meta:
        model = Option
        fields = ('option', 'correct')


class QuestionSerializer(serializers.ModelSerializer):
    options = OptionSerializer(many=True)

    class Meta:
        model = Question
        fields = ('lesson', 'question', 'options')

    def create(self, validated_data):
        options_data = validated_data.pop('options')
        question = Question.objects.create(**validated_data)
        for option_data in options_data:
            Option.objects.create(question=question, **option_data)
        return question

I'm try create test question with few option of answers like this:

data = {
    "lesson": 2,
    "question": "This is our first question?",
    "options": [
        {
            "option": "Option 1",
            "correct": True
        },
        {
            "option": "Option 2 ",
            "correct": False
        },
        {
            "option": "Option 3",
            "correct": True
        }
    ]
}

But in postman and in the TestCase I've got:

{
    "options": [
        "This field is required."
    ]
}

What is the problem of validation? Documentation here https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers Thanks.

Although I believe required=True inside OptionSerializer(many=True,required=True) should fix your issue if your json for OptionSerializer is correct else the following solution might sort out your problem

class QuestionSerializer(serializers.ModelSerializer):
    options = serializers.SerializerMethodField()

    class Meta:
        model = Question
        fields = ('lesson', 'question', 'options')

    def get_options(self, obj):
        options = Option.objects.filter(question=obj)
        serializer = OptionSerializer(options, many=True)
        return serializer.data

    def create(self, validated_data):
        options_data = validated_data.pop('options')
        question = Question.objects.create(**validated_data)
        for option_data in options_data:
            Option.objects.create(question=question, **option_data)
        return question

It does work perfectly in my testing. Just note your JSON object, you used True / False as Python boolean varible not JSONs: (Tested both with postman and DRF Browsable API)

data = {
    "lesson": 2,
    "question": "This is our first question?",
    "options": [
        {
            "option": "Option 1",
            "correct": true
        },
        {
            "option": "Option 2 ",
            "correct": false
        },
        {
            "option": "Option 3",
            "correct": true
        }
    ]
}
Back to Top