instance expected, got OrderedDict Django Rest Framework writable nested serializers

django-rest-framework, nested, serialization, writable

Solution

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.

Problem

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) ```

Original source