From 7f3fc4170509a3cf28c37297445249855e530648 Mon Sep 17 00:00:00 2001 From: "k.bar" Date: Thu, 22 Dec 2016 18:29:39 +0100 Subject: [PATCH 1/3] Fixed ListField empty check which ignored multi-part list elements --- rest_framework/fields.py | 9 ++++++--- rest_framework/utils/html.py | 5 ++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 13b5145ba..4d669ffb4 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1521,12 +1521,15 @@ class ListField(Field): self.child.bind(field_name='', parent=self) def get_value(self, dictionary): - if self.field_name not in dictionary: - if getattr(self.root, 'partial', False): - return empty # We override the default field access in order to support # lists in HTML forms. if html.is_html_input(dictionary): + if getattr(self.root, 'partial', False): + if self.field_name not in dictionary: + regexp = re.compile(html.HTML_LIST_REGEXP_FORMAT % re.escape(self.field_name)) + if not any(regexp.match(key) for key in dictionary.keys()): + return empty + val = dictionary.getlist(self.field_name, []) if len(val) > 0: # Support QueryDict lists in HTML input. diff --git a/rest_framework/utils/html.py b/rest_framework/utils/html.py index ca062c9fc..9f8113c53 100644 --- a/rest_framework/utils/html.py +++ b/rest_framework/utils/html.py @@ -6,6 +6,9 @@ import re from django.utils.datastructures import MultiValueDict +HTML_LIST_REGEXP_FORMAT = r'^%s\[([0-9]+)\](.*)$' + + def is_html_input(dictionary): # MultiDict type datastructures are used to represent HTML form input, # which may have more than one value for each key. @@ -46,7 +49,7 @@ def parse_html_list(dictionary, prefix=''): ] """ ret = {} - regex = re.compile(r'^%s\[([0-9]+)\](.*)$' % re.escape(prefix)) + regex = re.compile(HTML_LIST_REGEXP_FORMAT % re.escape(prefix)) for field, value in dictionary.items(): match = regex.match(field) if not match: From 281270a2cf25852ea19b310d7c1926bba006255d Mon Sep 17 00:00:00 2001 From: NeveHanter Date: Fri, 23 Dec 2016 19:05:51 +0100 Subject: [PATCH 2/3] Added tests --- tests/test_fields.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_fields.py b/tests/test_fields.py index 069ba879d..74ad36d0e 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -453,6 +453,24 @@ class TestHTMLInput: assert serializer.is_valid() assert serializer.validated_data == {'scores': [1]} + def test_querydict_indexed_list_input(self): + class TestSerializer(serializers.Serializer): + scores = serializers.ListField(child=serializers.IntegerField()) + + instance = {'scores': []} + serializer = TestSerializer(instance=instance, data=QueryDict('scores[0]=1&scores[1]=3'), partial=True) + assert serializer.is_valid() + assert serializer.validated_data == {'scores': [1, 3]} + + def test_querydict_indexed_list_only_one_input(self): + class TestSerializer(serializers.Serializer): + scores = serializers.ListField(child=serializers.IntegerField()) + + instance = {'scores': []} + serializer = TestSerializer(instance=instance, data=QueryDict('scores[0]=1'), partial=True) + assert serializer.is_valid() + assert serializer.validated_data == {'scores': [1]} + class TestCreateOnlyDefault: def setup(self): From 6e931f14758287ddfc0762e272aae31fa6996e29 Mon Sep 17 00:00:00 2001 From: NeveHanter Date: Fri, 23 Dec 2016 19:07:22 +0100 Subject: [PATCH 3/3] Renamed test case --- tests/test_fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_fields.py b/tests/test_fields.py index 74ad36d0e..93ca2706e 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -462,7 +462,7 @@ class TestHTMLInput: assert serializer.is_valid() assert serializer.validated_data == {'scores': [1, 3]} - def test_querydict_indexed_list_only_one_input(self): + def test_querydict_indexed_list_input_only_one_input(self): class TestSerializer(serializers.Serializer): scores = serializers.ListField(child=serializers.IntegerField())