Repository: encode/django-rest-framework

Issue:
Allow unexpected values for `ChoiceField`/`MultipleChoiceField` representations.
When an invalid value is present in the database that isn't part of the list of choices (perhaps dealing with a legacy database) a KeyError will be raised in to_representation() of ChoiceField/MultipleChoiceField.

Personally I'd like to see the behaviour more in line with the Django ORM's choices behaviour where the invalid value is still readable, perhaps by simply returning it from to_representation()

In case this is disagreeable, I think the KeyError should be caught and a new exception raised with a more easily debuggable error message explaining which field has the invalid value.



Discussion:
Totally agree!
It breaks down the system in such cases.
I think simply returning "invalid" value would be the best.

Additionally, the current behaviour does not allow to have one set of fields for the output and another for input. Eg. we have a 3 different values possible on the model layer, but I only want to accept 2 of them in the input of a specific serializer (yet return all 3 of them in the output if they're there). Solvable with a custom field, but still a pain.

> I think the KeyError should be caught and a new exception raised with a more easily debuggable error message explaining which field has the invalid value.

Happy to accept an issue for this if it's presented as an example with:
- The current behavior.
- The suggested behavior.

As far as the remainder of the ticket - it's unclear to me what's being suggested, but it sounds like a case for a custom field, as does @maryokhin's different input and output requirement.

The key of the current behaviour is difference from Django ORM behaviour.
Using Django ORM you can have in db field values that not presents in field choices.
And this is not rare case! 
Your db can be legacy or in the middle of the data migrations or smth else.
But this is not rare.
Using Django you can safety deal with this case - those field still readable.
You just get value as-is.
Using DRF you get not operational api with strange and useless for the first time KeyError exception.

Ah, misunderstood the ticket here I think - so the request is that unexpected values should be passed through as-is to the representation. Sure I've no great problem with that. Happy to accept a pull request modifying the behavior there, and including an example test case.

Exactly!
Accepted.


Resolving patch:
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -1045,7 +1045,7 @@ def to_internal_value(self, data):
     def to_representation(self, value):
         if value in ('', None):
             return value
-        return self.choice_strings_to_values[six.text_type(value)]
+        return self.choice_strings_to_values.get(six.text_type(value), value)
 
 
 class MultipleChoiceField(ChoiceField):
@@ -1073,7 +1073,7 @@ def to_internal_value(self, data):
 
     def to_representation(self, value):
         return set([
-            self.choice_strings_to_values[six.text_type(item)] for item in value
+            self.choice_strings_to_values.get(six.text_type(item), item) for item in value
         ])
 
 


Test patch:
diff --git a/tests/test_fields.py b/tests/test_fields.py
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -920,7 +920,8 @@ class TestChoiceField(FieldValues):
     }
     outputs = {
         'good': 'good',
-        '': ''
+        '': '',
+        'amazing': 'amazing',
     }
     field = serializers.ChoiceField(
         choices=[
@@ -1005,7 +1006,7 @@ class TestMultipleChoiceField(FieldValues):
         ('aircon', 'incorrect'): ['"incorrect" is not a valid choice.']
     }
     outputs = [
-        (['aircon', 'manual'], set(['aircon', 'manual']))
+        (['aircon', 'manual', 'incorrect'], set(['aircon', 'manual', 'incorrect']))
     ]
     field = serializers.MultipleChoiceField(
         choices=[