From 3ae711f3ab92668693fb216c7252bbfcb6a0d20d Mon Sep 17 00:00:00 2001 From: Craig de Stigter Date: Thu, 28 Jul 2016 12:52:14 +1200 Subject: [PATCH] Return Empty when getting listserializers from empty html input If a non-required nested list serializer is *not* included in postdata from an HTML form, the field validation nonetheless returns an empty list. This means you can easily clobber existing data when PUTting to an existing resource, just by not including some list fields. This is made worse by #3647, in which empty-body JSON requests will also be treated as HTML forms, meaning any optional list fields will get overwritten by an empty list. --- rest_framework/serializers.py | 2 +- tests/test_serializer_lists.py | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index d5e6b66ed..80670026e 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -545,7 +545,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_lists.py b/tests/test_serializer_lists.py index 607ddba04..833b93856 100644 --- a/tests/test_serializer_lists.py +++ b/tests/test_serializer_lists.py @@ -146,7 +146,7 @@ class TestNestedListSerializer: def setup(self): class TestSerializer(serializers.Serializer): - integers = serializers.ListSerializer(child=serializers.IntegerField()) + integers = serializers.ListSerializer(child=serializers.IntegerField(), required=False) booleans = serializers.ListSerializer(child=serializers.BooleanField()) def create(self, validated_data): @@ -221,6 +221,22 @@ class TestNestedListSerializer: assert serializer.is_valid() assert serializer.validated_data == expected_output + def test_validate_empty_html_input(self): + """ + When a field isn't present in HTML input, the field isn't included + in the output. + """ + input_data = MultiValueDict({ + "booleans[0]": ["true"], + "booleans[1]": ["false"] + }) + expected_output = { + "booleans": [True, False] + } + serializer = self.Serializer(data=input_data) + assert serializer.is_valid() + assert serializer.validated_data == expected_output + class TestNestedListOfListsSerializer: def setup(self):