mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-07-27 08:29:59 +03:00
Add strict_choices to ChoiceField & MultipleChoiceField
This commit is contained in:
parent
61e7a993bd
commit
1a2142e1f5
|
@ -404,6 +404,7 @@ Used by `ModelSerializer` to automatically generate fields if the corresponding
|
||||||
- `allow_blank` - If set to `True` then the empty string should be considered a valid value. If set to `False` then the empty string is considered invalid and will raise a validation error. Defaults to `False`.
|
- `allow_blank` - If set to `True` then the empty string should be considered a valid value. If set to `False` then the empty string is considered invalid and will raise a validation error. Defaults to `False`.
|
||||||
- `html_cutoff` - If set this will be the maximum number of choices that will be displayed by a HTML select drop down. Can be used to ensure that automatically generated ChoiceFields with very large possible selections do not prevent a template from rendering. Defaults to `None`.
|
- `html_cutoff` - If set this will be the maximum number of choices that will be displayed by a HTML select drop down. Can be used to ensure that automatically generated ChoiceFields with very large possible selections do not prevent a template from rendering. Defaults to `None`.
|
||||||
- `html_cutoff_text` - If set this will display a textual indicator if the maximum number of items have been cutoff in an HTML select drop down. Defaults to `"More than {count} items…"`
|
- `html_cutoff_text` - If set this will display a textual indicator if the maximum number of items have been cutoff in an HTML select drop down. Defaults to `"More than {count} items…"`
|
||||||
|
- `strict_choices` - If set to `True` then `to_representation()` checks if output is one of choices. Defaults to `False`.
|
||||||
|
|
||||||
Both the `allow_blank` and `allow_null` are valid options on `ChoiceField`, although it is highly recommended that you only use one and not both. `allow_blank` should be preferred for textual choices, and `allow_null` should be preferred for numeric or other non-textual choices.
|
Both the `allow_blank` and `allow_null` are valid options on `ChoiceField`, although it is highly recommended that you only use one and not both. `allow_blank` should be preferred for textual choices, and `allow_null` should be preferred for numeric or other non-textual choices.
|
||||||
|
|
||||||
|
@ -417,6 +418,7 @@ A field that can accept a set of zero, one or many values, chosen from a limited
|
||||||
- `allow_blank` - If set to `True` then the empty string should be considered a valid value. If set to `False` then the empty string is considered invalid and will raise a validation error. Defaults to `False`.
|
- `allow_blank` - If set to `True` then the empty string should be considered a valid value. If set to `False` then the empty string is considered invalid and will raise a validation error. Defaults to `False`.
|
||||||
- `html_cutoff` - If set this will be the maximum number of choices that will be displayed by a HTML select drop down. Can be used to ensure that automatically generated ChoiceFields with very large possible selections do not prevent a template from rendering. Defaults to `None`.
|
- `html_cutoff` - If set this will be the maximum number of choices that will be displayed by a HTML select drop down. Can be used to ensure that automatically generated ChoiceFields with very large possible selections do not prevent a template from rendering. Defaults to `None`.
|
||||||
- `html_cutoff_text` - If set this will display a textual indicator if the maximum number of items have been cutoff in an HTML select drop down. Defaults to `"More than {count} items…"`
|
- `html_cutoff_text` - If set this will display a textual indicator if the maximum number of items have been cutoff in an HTML select drop down. Defaults to `"More than {count} items…"`
|
||||||
|
- `strict_choices` - If set to `True` then `to_representation()` checks if output is a set from choices. Defaults to `False`.
|
||||||
|
|
||||||
As with `ChoiceField`, both the `allow_blank` and `allow_null` options are valid, although it is highly recommended that you only use one and not both. `allow_blank` should be preferred for textual choices, and `allow_null` should be preferred for numeric or other non-textual choices.
|
As with `ChoiceField`, both the `allow_blank` and `allow_null` options are valid, although it is highly recommended that you only use one and not both. `allow_blank` should be preferred for textual choices, and `allow_null` should be preferred for numeric or other non-textual choices.
|
||||||
|
|
||||||
|
|
|
@ -1416,6 +1416,7 @@ class ChoiceField(Field):
|
||||||
self.html_cutoff_text = kwargs.pop('html_cutoff_text', self.html_cutoff_text)
|
self.html_cutoff_text = kwargs.pop('html_cutoff_text', self.html_cutoff_text)
|
||||||
|
|
||||||
self.allow_blank = kwargs.pop('allow_blank', False)
|
self.allow_blank = kwargs.pop('allow_blank', False)
|
||||||
|
self.strict_choices = kwargs.pop('strict_choices', False)
|
||||||
|
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
@ -1429,6 +1430,12 @@ class ChoiceField(Field):
|
||||||
self.fail('invalid_choice', input=data)
|
self.fail('invalid_choice', input=data)
|
||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
|
if self.strict_choices:
|
||||||
|
try:
|
||||||
|
return self.choice_strings_to_values[str(value)]
|
||||||
|
except KeyError:
|
||||||
|
self.fail('invalid_choice', input=value)
|
||||||
|
|
||||||
if value in ('', None):
|
if value in ('', None):
|
||||||
return value
|
return value
|
||||||
return self.choice_strings_to_values.get(str(value), value)
|
return self.choice_strings_to_values.get(str(value), value)
|
||||||
|
@ -1494,6 +1501,17 @@ class MultipleChoiceField(ChoiceField):
|
||||||
}
|
}
|
||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
|
if self.strict_choices:
|
||||||
|
if isinstance(value, str) or not hasattr(value, '__iter__'):
|
||||||
|
self.fail('not_a_list', input_type=type(value).__name__)
|
||||||
|
if not self.allow_empty and len(value) == 0:
|
||||||
|
self.fail('empty')
|
||||||
|
|
||||||
|
return {
|
||||||
|
super(MultipleChoiceField, self).to_representation(item)
|
||||||
|
for item in value
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
self.choice_strings_to_values.get(str(item), item) for item in value
|
self.choice_strings_to_values.get(str(item), item) for item in value
|
||||||
}
|
}
|
||||||
|
|
|
@ -1693,6 +1693,23 @@ class TestChoiceField(FieldValues):
|
||||||
field.run_validation(2)
|
field.run_validation(2)
|
||||||
assert exc_info.value.detail == ['"2" is not a valid choice.']
|
assert exc_info.value.detail == ['"2" is not a valid choice.']
|
||||||
|
|
||||||
|
def test_strict_choices(self):
|
||||||
|
"""
|
||||||
|
If `strict_choices` then to_representation() should return one of given choices.
|
||||||
|
"""
|
||||||
|
field = serializers.ChoiceField(
|
||||||
|
strict_choices=True,
|
||||||
|
choices=[
|
||||||
|
('poor', 'Poor quality'),
|
||||||
|
('medium', 'Medium quality'),
|
||||||
|
('good', 'Good quality'),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
assert field.to_representation('poor') == 'poor'
|
||||||
|
with pytest.raises(serializers.ValidationError) as exc_info:
|
||||||
|
field.to_representation('amazing')
|
||||||
|
assert exc_info.value.detail == ['"amazing" is not a valid choice.']
|
||||||
|
|
||||||
|
|
||||||
class TestChoiceFieldWithType(FieldValues):
|
class TestChoiceFieldWithType(FieldValues):
|
||||||
"""
|
"""
|
||||||
|
@ -1830,6 +1847,24 @@ class TestMultipleChoiceField(FieldValues):
|
||||||
field.partial = True
|
field.partial = True
|
||||||
assert field.get_value(QueryDict({})) == rest_framework.fields.empty
|
assert field.get_value(QueryDict({})) == rest_framework.fields.empty
|
||||||
|
|
||||||
|
def test_multiple_strict_choices(self):
|
||||||
|
"""
|
||||||
|
If `strict_choices` then to_representation() should return a set from given choices.
|
||||||
|
"""
|
||||||
|
field = serializers.MultipleChoiceField(
|
||||||
|
strict_choices=True,
|
||||||
|
choices=[
|
||||||
|
('aircon', 'AirCon'),
|
||||||
|
('manual', 'Manual drive'),
|
||||||
|
('diesel', 'Diesel'),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert field.to_representation(['aircon', 'manual']) == {'aircon', 'manual'}
|
||||||
|
with pytest.raises(serializers.ValidationError) as exc_info:
|
||||||
|
field.to_representation(['aircon', 'incorrect'])
|
||||||
|
assert exc_info.value.detail == ['"incorrect" is not a valid choice.']
|
||||||
|
|
||||||
|
|
||||||
class TestEmptyMultipleChoiceField(FieldValues):
|
class TestEmptyMultipleChoiceField(FieldValues):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user