Skip to content

Commit 22ffeaf

Browse files
committed
Merge pull request #2311 from tomchristie/allow-empty-text-or-boolean-html-input-if-not-required
Fixes for behavior with empty HTML fields.
2 parents e8b4641 + 1087ccb commit 22ffeaf

File tree

2 files changed

+44
-19
lines changed

2 files changed

+44
-19
lines changed

rest_framework/fields.py

+10-7
Original file line numberDiff line numberDiff line change
@@ -184,13 +184,11 @@ def __init__(self, read_only=False, write_only=False,
184184
self.style = {} if style is None else style
185185
self.allow_null = allow_null
186186

187-
if allow_null and self.default_empty_html is empty:
188-
# HTML input cannot represent `None` values, so we need to
189-
# forcibly coerce empty HTML values to `None` if `allow_null=True`.
190-
self.default_empty_html = None
191-
192-
if default is not empty:
193-
self.default_empty_html = default
187+
if self.default_empty_html is not empty:
188+
if not required:
189+
self.default_empty_html = empty
190+
elif default is not empty:
191+
self.default_empty_html = default
194192

195193
if validators is not None:
196194
self.validators = validators[:]
@@ -562,6 +560,11 @@ def __init__(self, **kwargs):
562560
message = self.error_messages['min_length'].format(min_length=min_length)
563561
self.validators.append(MinLengthValidator(min_length, message=message))
564562

563+
if self.allow_null and (not self.allow_blank) and (self.default is empty):
564+
# HTML input cannot represent `None` values, so we need to
565+
# forcibly coerce empty HTML values to `None` if `allow_null=True`.
566+
self.default_empty_html = None
567+
565568
def run_validation(self, data=empty):
566569
# Test for the empty string here so that it does not get validated,
567570
# and so that subclasses do not need to handle it explicitly

tests/test_fields.py

+34-12
Original file line numberDiff line numberDiff line change
@@ -215,25 +215,47 @@ class MockHTMLDict(dict):
215215
assert serializer.validated_data == {'archived': False}
216216

217217

218+
class MockHTMLDict(dict):
219+
"""
220+
This class mocks up a dictionary like object, that behaves
221+
as if it was returned for multipart or urlencoded data.
222+
"""
223+
getlist = None
224+
225+
218226
class TestCharHTMLInput:
219-
def setup(self):
227+
def test_empty_html_checkbox(self):
220228
class TestSerializer(serializers.Serializer):
221229
message = serializers.CharField(default='happy')
222-
self.Serializer = TestSerializer
223230

224-
def test_empty_html_checkbox(self):
225-
"""
226-
HTML checkboxes do not send any value, but should be treated
227-
as `False` by BooleanField.
228-
"""
229-
# This class mocks up a dictionary like object, that behaves
230-
# as if it was returned for multipart or urlencoded data.
231-
class MockHTMLDict(dict):
232-
getlist = None
233-
serializer = self.Serializer(data=MockHTMLDict())
231+
serializer = TestSerializer(data=MockHTMLDict())
234232
assert serializer.is_valid()
235233
assert serializer.validated_data == {'message': 'happy'}
236234

235+
def test_empty_html_checkbox_allow_null(self):
236+
class TestSerializer(serializers.Serializer):
237+
message = serializers.CharField(allow_null=True)
238+
239+
serializer = TestSerializer(data=MockHTMLDict())
240+
assert serializer.is_valid()
241+
assert serializer.validated_data == {'message': None}
242+
243+
def test_empty_html_checkbox_allow_null_allow_blank(self):
244+
class TestSerializer(serializers.Serializer):
245+
message = serializers.CharField(allow_null=True, allow_blank=True)
246+
247+
serializer = TestSerializer(data=MockHTMLDict({}))
248+
assert serializer.is_valid()
249+
assert serializer.validated_data == {'message': ''}
250+
251+
def test_empty_html_required_false(self):
252+
class TestSerializer(serializers.Serializer):
253+
message = serializers.CharField(required=False)
254+
255+
serializer = TestSerializer(data=MockHTMLDict())
256+
assert serializer.is_valid()
257+
assert serializer.validated_data == {}
258+
237259

238260
class TestCreateOnlyDefault:
239261
def setup(self):

0 commit comments

Comments
 (0)