Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion rest_framework/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -1754,7 +1754,14 @@ def get_value(self, dictionary):
# 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, default=empty)
result = html.parse_html_dict(dictionary, prefix=self.field_name, default=empty)
if result is not empty:
return result
# If the field name itself is present in the input,
# treat it as an explicit empty dict (e.g. clearing the field).
if self.field_name in dictionary:
return {}
return empty
return dictionary.get(self.field_name, empty)

def to_internal_value(self, data):
Expand Down
26 changes: 22 additions & 4 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2522,7 +2522,7 @@ def test_allow_empty_disallowed(self):

assert exc_info.value.detail == ['This dictionary may not be empty.']

def test_querydict_dict_input(self):
def test_query_dict_input_with_dot_separated_keys(self):
"""
DictField should correctly parse HTML form (QueryDict) input
with dot-separated keys.
Expand All @@ -2534,7 +2534,7 @@ class TestSerializer(serializers.Serializer):
assert serializer.is_valid(), serializer.errors
assert serializer.validated_data == {'data': {'a': '1', 'b': '2'}}

def test_querydict_dict_input_no_values_uses_default(self):
def test_query_dict_input_no_values_uses_default(self):
"""
When no matching keys are present in the QueryDict and a default
is set, the field should return the default value.
Expand All @@ -2547,7 +2547,7 @@ class TestSerializer(serializers.Serializer):
assert serializer.is_valid(), serializer.errors
assert serializer.validated_data == {'a': 1, 'data': {'x': 'y'}}

def test_querydict_dict_input_no_values_no_default_and_not_required(self):
def test_query_dict_input_no_values_no_default_and_not_required(self):
"""
When no matching keys are present in the QueryDict, there is no
default, and the field is not required, the field should be
Expand All @@ -2560,7 +2560,7 @@ class TestSerializer(serializers.Serializer):
assert serializer.is_valid(), serializer.errors
assert serializer.validated_data == {}

def test_querydict_dict_input_no_values_required(self):
def test_query_dict_input_no_values_required(self):
"""
When no matching keys are present in the QueryDict and the field
is required, validation should fail.
Expand All @@ -2572,6 +2572,24 @@ class TestSerializer(serializers.Serializer):
assert not serializer.is_valid()
assert 'data' in serializer.errors

def test_partial_update_can_clear_html_dict_field(self):
"""
Test that a partial update can clear a DictField when provided with an
empty string value through a QueryDict.
"""
class TestSerializer(serializers.Serializer):
field_name = serializers.DictField(required=False)
other_field = serializers.CharField(required=False)

serializer = TestSerializer(
data=QueryDict('field_name='),
partial=True,
)
assert serializer.is_valid()
assert 'field_name' in serializer.validated_data
assert serializer.validated_data['field_name'] == {}
assert 'other_field' not in serializer.validated_data


class TestNestedDictField(FieldValues):
"""
Expand Down