instance expected, got OrderedDict Django Rest Framework writable nested serializers
Asked Answered
C

1

6

I am creating a survey kind of app, so i have three models Form, Questiosn, Choices[for multiple choice questions]

I followed this tutorial http://www.django-rest-framework.org/api-guide/relations/#nested-relationships

It works fine for 1 level nested relations, but for 2 levels it gives

TypeError: 'Choice' instance expected, got OrderedDict([(u'title', u'option1')])

class ChoiceSerializer(serializers.ModelSerializer):

    class Meta:
        model = Choice
        fields = ['title']

class QuestionSerializer(serializers.ModelSerializer):
    choices = ChoiceSerializer(many=True, required=False)

    class Meta:
    model = Question
    fields = ['title', 'type', 'required','order','choices']

    def create(self, validated_data):
    choices_data = validated_data.pop("choices")
    question = Question.objects.create(**validated_data)
    for choice_data in choices_data:
        Choice.objects.create(question=question, **choice_data)
    return question

class FormSerializer(serializers.ModelSerializer):
    questions = QuestionSerializer(many=True)

    class Meta:
    model = Form
    fields = ['title', 'description', 'created', 'active', 'hash','questions']
    read_only_fields = ['active','hash']

    def create(self, validated_data):
    questions_data = validated_data.pop('questions')
    form = Form.objects.create(**validated_data)
    for question_data in questions_data:
        Question.objects.create(form=form, **question_data)
    return form

EDIT

Solved using the manual way, In FormSerializer override the create method,

@transaction.atomic
    def create(self, validated_data):
        try:
            with transaction.atomic():
                questions_data = validated_data.pop('questions')
                form = Form.objects.create(**validated_data)
                for question_data in questions_data:
                    question = Question.objects.create(form=form,
                                                       title=question_data['title'],
                                                       type=question_data['type'],
                                                       required=question_data['required'])
                    if question.type == Question.RADIO or question.type == Question.CHECKBOX:
                        choices_data = question_data.pop('choices')
                        for choice_data in choices_data:
                            choice = Choice.objects.create(question=question, title=choice_data['title'])
                return form
        except Exception, e:
            raise serializers.ValidationError("Cannot Save Form %s" % e)
Clevelandclevenger answered 20/10, 2016 at 23:28 Comment(1)
Since I was not going any where with this, I did it the manual wayClevelandclevenger
H
6

I also struggled with this and I believe the proper way to handle this is:

class ChoiceSerializer(serializers.ModelSerializer):

    class Meta:
        model = Choice
        fields = ['title']

class QuestionSerializer(serializers.ModelSerializer):
    choices = ChoiceSerializer(many=True, required=False)

    class Meta:
        model = Question
        fields = ['title', 'type', 'required','order','choices']


class FormSerializer(serializers.ModelSerializer):
    questions = QuestionSerializer(many=True)

    class Meta:
        model = Form
        fields = ['title', 'description', 'created', 'active', 
                 'hash','questions']
        read_only_fields = ['active','hash']

    def create(self, validated_data):
        questions_data = validated_data.pop('questions')
        form = Form.objects.create(**validated_data)
        for question_data in questions_data:
            choices_data = question_data.pop('choices')
            Question.objects.create(form=form, **question_data)
            for choice_data in choices_data:
                choice = Choice.objects.create(question=question, **choice_data)
        return form

An easy way to screw this up is to not pop choices before creating the Question object. When you do that, you'll get an instance expected, got OrderedDict( 500 error.

Note also that you do not need to define create() on the QuestionSerializer. All child processing is done at the top level.

Henna answered 28/11, 2016 at 21:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.