diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 3ca7d682e..09eacd3a2 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1274,7 +1274,7 @@ class ListField(Field): # We override the default field access in order to support # lists in HTML forms. if html.is_html_input(dictionary): - return html.parse_html_list(dictionary, prefix=self.field_name) + return html.parse_html_list(dictionary, prefix=self.field_name) or empty return dictionary.get(self.field_name, empty) def to_internal_value(self, data): @@ -1311,7 +1311,7 @@ class DictField(Field): # We override the default field access in order to support # dictionaries in HTML forms. if html.is_html_input(dictionary): - return html.parse_html_dict(dictionary, prefix=self.field_name) + return html.parse_html_dict(dictionary, prefix=self.field_name) or empty return dictionary.get(self.field_name, empty) def to_internal_value(self, data): diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 7afddfd82..1867de957 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -367,7 +367,7 @@ class Serializer(BaseSerializer): # We override the default field access in order to support # nested HTML forms. if html.is_html_input(dictionary): - return html.parse_html_dict(dictionary, prefix=self.field_name) + return html.parse_html_dict(dictionary, prefix=self.field_name) or empty return dictionary.get(self.field_name, empty) def run_validation(self, data=empty): @@ -515,7 +515,7 @@ class ListSerializer(BaseSerializer): # We override the default field access in order to support # lists in HTML forms. if html.is_html_input(dictionary): - return html.parse_html_list(dictionary, prefix=self.field_name) + return html.parse_html_list(dictionary, prefix=self.field_name) or empty return dictionary.get(self.field_name, empty) def run_validation(self, data=empty): diff --git a/tests/test_serializer_nested.py b/tests/test_serializer_nested.py index f5e4b26ad..103f2588d 100644 --- a/tests/test_serializer_nested.py +++ b/tests/test_serializer_nested.py @@ -38,3 +38,70 @@ class TestNestedSerializer: } serializer = self.Serializer() assert serializer.data == expected_data + + +class TestRequiredFalseNestedSerializer: + def setup(self): + class NestedSerializer(serializers.Serializer): + one = serializers.IntegerField(max_value=10) + + class TestSerializer(serializers.Serializer): + nested = NestedSerializer(required=False) + + self.Serializer = TestSerializer + + class FakeMultiDict(dict): + """ + Use this to fake a `format="multipart"` request, because + `utils.is_html_input()` returns `True` when the dict object has + an attribute of "getlist". + """ + def getlist(self, value, default=None): + if value in self: + return [self[value]] + else: + return [] if default is None else default + + self.FakeMultiDict = FakeMultiDict + + def test_nested_json_validate(self): + input_data = { + 'nested': { + 'one': '1', + }, + } + expected_data = { + 'nested': { + 'one': 1, + }, + } + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.validated_data == expected_data + + def test_missing_nested_json_validate(self): + input_data = {} + expected_data = {} + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.validated_data == expected_data + + def test_nested_multipart_validate(self): + input_data = self.FakeMultiDict(**{ + 'nested.one': '1', + }) + expected_data = { + 'nested': { + 'one': 1, + }, + } + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.validated_data == expected_data + + def test_missing_nested_multipart_validate(self): + input_data = self.FakeMultiDict() + expected_data = {} + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.validated_data == expected_data