Skip to content
Open
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
5 changes: 4 additions & 1 deletion rest_framework/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -1754,7 +1754,10 @@ 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)
value = html.parse_html_dict(dictionary, prefix=self.field_name)
if not value and getattr(self.root, 'partial', False):
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DictField.get_value() still returns an empty dict for omitted HTML/multipart input when partial=False. That means omitted DictFields can bypass the normal empty sentinel handling (e.g., required=True won’t raise “required”, and required=False fields can incorrectly show up in validated_data as {}). Consider matching Serializer.get_value()/ListField.get_value() behavior by returning empty whenever parse_html_dict(...) is empty (e.g., ... or empty), not only on partial updates.

Suggested change
if not value and getattr(self.root, 'partial', False):
if not value:

Copilot uses AI. Check for mistakes.
return empty
return value
return dictionary.get(self.field_name, empty)

def to_internal_value(self, data):
Expand Down
8 changes: 8 additions & 0 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2522,6 +2522,14 @@ def test_allow_empty_disallowed(self):

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

def test_partial_update_does_not_include_missing_html_dict_field(self):
class TestSerializer(serializers.Serializer):
field_name = serializers.DictField(required=False)

serializer = TestSerializer(data=QueryDict(''), partial=True)
assert serializer.is_valid()
assert 'field_name' not in serializer.validated_data
Comment on lines +2525 to +2531
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to increate test coverage... This test passes on the main branch, but breaks here:

    def test_partial_update_can_clear_html_dict_field(self):
        class TestSerializer(serializers.Serializer):
            field_name = serializers.DictField(required=False)

        serializer = TestSerializer(data=QueryDict('field_name='), partial=True)
        assert serializer.is_valid()
        assert 'field_name' in serializer.validated_data



Comment on lines +2532 to 2533
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new regression test covers partial updates, but the underlying omission behavior for HTML dict input can also affect full updates and required=True fields (omitted field being treated as {} instead of missing). Add a complementary test case for partial=False (and ideally required=True) to lock in the expected behavior once DictField.get_value() is adjusted.

Suggested change
def test_full_update_missing_required_html_dict_field_is_error(self):
class TestSerializer(serializers.Serializer):
field_name = serializers.DictField(required=True)
serializer = TestSerializer(data=QueryDict(''), partial=False)
assert not serializer.is_valid()
assert 'field_name' in serializer.errors

Copilot uses AI. Check for mistakes.
class TestNestedDictField(FieldValues):
"""
Expand Down
Loading