Skip to content

Commit

Permalink
Return Empty when getting listserializers from empty html input
Browse files Browse the repository at this point in the history
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 DRF treating skipping request parsing for empty input.
- encode#3647 and encode#4566 are "fixed" but do not appear to fix the problem;
empty JSON posts still skip request parsing, and list fields not included in
the post still get set to empty lists.
  • Loading branch information
craigds committed May 9, 2018
1 parent b615042 commit de82a51
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 2 deletions.
2 changes: 1 addition & 1 deletion rest_framework/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ def get_value(self, dictionary):
# 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):
Expand Down
18 changes: 17 additions & 1 deletion tests/test_serializer_lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -221,6 +221,22 @@ def test_validate_html_input(self):
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):
Expand Down

0 comments on commit de82a51

Please sign in to comment.