Skip to content

Commit afe7ed9

Browse files
committed
Add allow_blank for ChoiceField #2184
This makes a ChoiceField optional in HTML if model field has `blank=True` set.
1 parent 0f080bc commit afe7ed9

File tree

7 files changed

+30
-10
lines changed

7 files changed

+30
-10
lines changed

rest_framework/fields.py

+5
Original file line numberDiff line numberDiff line change
@@ -958,9 +958,14 @@ def __init__(self, choices, **kwargs):
958958
(six.text_type(key), key) for key in self.choices.keys()
959959
])
960960

961+
self.allow_blank = kwargs.pop('allow_blank', False)
962+
961963
super(ChoiceField, self).__init__(**kwargs)
962964

963965
def to_internal_value(self, data):
966+
if data == '' and self.allow_blank:
967+
return ''
968+
964969
try:
965970
return self.choice_strings_to_values[six.text_type(data)]
966971
except KeyError:

rest_framework/serializers.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -942,7 +942,7 @@ def get_fields(self):
942942
# `ModelField`, which is used when no other typed field
943943
# matched to the model field.
944944
kwargs.pop('model_field', None)
945-
if not issubclass(field_cls, CharField):
945+
if not issubclass(field_cls, CharField) and not issubclass(field_cls, ChoiceField):
946946
# `allow_blank` is only valid for textual fields.
947947
kwargs.pop('allow_blank', None)
948948

rest_framework/templates/rest_framework/horizontal/select.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
{% endif %}
55
<div class="col-sm-10">
66
<select class="form-control" name="{{ field.name }}">
7-
{% if field.allow_null %}
7+
{% if field.allow_null or field.allow_blank %}
88
<option value="" {% if not field.value %}selected{% endif %}>--------</option>
99
{% endif %}
1010
{% for key, text in field.choices.items %}

rest_framework/templates/rest_framework/inline/select.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<label class="sr-only">{{ field.label }}</label>
44
{% endif %}
55
<select class="form-control" name="{{ field.name }}">
6-
{% if field.allow_null %}
6+
{% if field.allow_null or field.allow_blank %}
77
<option value="" {% if not field.value %}selected{% endif %}>--------</option>
88
{% endif %}
99
{% for key, text in field.choices.items %}

rest_framework/templates/rest_framework/vertical/select.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<label {% if style.hide_label %}class="sr-only"{% endif %}>{{ field.label }}</label>
44
{% endif %}
55
<select class="form-control" name="{{ field.name }}">
6-
{% if field.allow_null %}
6+
{% if field.allow_null or field.allow_blank %}
77
<option value="" {% if not field.value %}selected{% endif %}>--------</option>
88
{% endif %}
99
{% for key, text in field.choices.items %}

rest_framework/utils/field_mapping.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -91,18 +91,18 @@ def get_field_kwargs(field_name, model_field):
9191
if model_field.has_default() or model_field.blank or model_field.null:
9292
kwargs['required'] = False
9393

94-
if model_field.flatchoices:
95-
# If this model field contains choices, then return early.
96-
# Further keyword arguments are not valid.
97-
kwargs['choices'] = model_field.flatchoices
98-
return kwargs
99-
10094
if model_field.null and not isinstance(model_field, models.NullBooleanField):
10195
kwargs['allow_null'] = True
10296

10397
if model_field.blank:
10498
kwargs['allow_blank'] = True
10599

100+
if model_field.flatchoices:
101+
# If this model field contains choices, then return early.
102+
# Further keyword arguments are not valid.
103+
kwargs['choices'] = model_field.flatchoices
104+
return kwargs
105+
106106
# Ensure that max_length is passed explicitly as a keyword arg,
107107
# rather than as a validator.
108108
max_length = getattr(model_field, 'max_length', None)

tests/test_fields.py

+15
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,21 @@ class TestChoiceField(FieldValues):
804804
]
805805
)
806806

807+
def test_allow_blank(self):
808+
"""
809+
If `allow_blank=True` then '' is a valid input.
810+
"""
811+
field = serializers.ChoiceField(
812+
allow_blank=True,
813+
choices=[
814+
('poor', 'Poor quality'),
815+
('medium', 'Medium quality'),
816+
('good', 'Good quality'),
817+
]
818+
)
819+
output = field.run_validation('')
820+
assert output is ''
821+
807822

808823
class TestChoiceFieldWithType(FieldValues):
809824
"""

0 commit comments

Comments
 (0)