John
John

Reputation: 6065

Key error in Django Rest Framework when using serializers.ChoiceField with tuples

I am using Django 1.7.7 and Django Rest Framework 3.1.1.

When I serialize this model

class Question(models.Model):
  QUESTION_TYPES = (
    (10,'Blurb'),
    (20,'Group Header'),
    (21,'Group Footer'),
    (30,'Sub-Group Header'),
    (31,'Sub-Group Footer'),
    (50,'Save Button'),
    (100,'Standard Question'),
    (105,'Text-Area Question'),
    (110,'Multiple-Choice Question'),
    (120,'Standard Sub-Question'),
    (130,'Multiple-Choice Sub-Question')
)
type = models.IntegerField(default=100,choices=QUESTION_TYPES)

using this viewset/serializer:

class QuestionSerializer(serializers.ModelSerializer):
   type = serializers.ChoiceField(choices='QUESTION_TYPES')

   class Meta:
      model = Question    

class QuestionViewSet(viewsets.ModelViewSet):
   model = Question
   serializer_class = QuestionSerializer

   def get_queryset(self):
      return Question.objects.all()

I get a KeyError '10' (or whatever QUESTION_TYPES key is the first to be serialized from the Questions table).

The error seems to be thrown by rest_framework/fields.py in to_representation return self.choice_strings_to_values[six.text_type(value)]

Is there anything obvious I am doing wrong? Is there a problem using tuples with serializer.ChoiceField?

John

Upvotes: 1

Views: 3492

Answers (3)

moisesgallego
moisesgallego

Reputation: 421

The equivalent on Python 3

self.choice_strings_to_values = dict([
    (six.text_type(key), str(value)) for key, value in self.choices.items()
])

Upvotes: 1

Felipe
Felipe

Reputation: 440

It all comes down to this line in the ChoiceField class. It uses the key twice.

self.choice_strings_to_values = dict([
    (six.text_type(key), key) for key in self.choices.keys()
])

You can change this behavior by creating your own field serializer extending the ChoiceField class

class DisplayChoiceFieldSerializers(serializers.ChoiceField):
    def __init__(self, *args, **kwargs):
        super(DisplayChoiceFieldSerializers, self).__init__(*args, **kwargs)
        self.choice_strings_to_values = dict([
            (six.text_type(key), unicode(value)) for key, value in self.choices.iteritems()
        ])

Upvotes: 2

mariodev
mariodev

Reputation: 15519

Seems like ChoiceField has some problems when trying to override the behavior in the serializer itself.

You can go around this though by using two separate fields:

class QuestionSerializer(serializers.ModelSerializer):
    type = serializers.ChoiceField(choices=Question.QUESTION_TYPES)
    type_display = serializers.CharField(source='get_type_display',
                                         read_only=True)

    class Meta:
        model = Question

Upvotes: 2

Related Questions