Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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: 5 additions & 4 deletions rest_framework/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,18 +688,19 @@ def to_internal_value(self, data):
}, code='min_length')

ret = []
errors = []
errors = {}
index = 0

for item in data:
try:
validated = self.run_child_validation(item)
except ValidationError as exc:
errors.append(exc.detail)
errors[index] = exc.detail
else:
ret.append(validated)
errors.append({})
index += 1

if any(errors):
if errors:
raise ValidationError(errors)

return ret
Expand Down
14 changes: 4 additions & 10 deletions tests/test_serializer_bulk_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,9 @@ def test_bulk_create_errors(self):
'author': 'Haruki Murakami'
}
]
expected_errors = [
{},
{},
{'id': ['A valid integer is required.']}
]
expected_errors = {
2: {'id': ['A valid integer is required.']}
}

serializer = self.BookSerializer(data=data, many=True)
assert serializer.is_valid() is False
Expand All @@ -85,11 +83,7 @@ def test_invalid_list_datatype(self):
assert serializer.is_valid() is False

message = 'Invalid data. Expected a dictionary, but got str.'
expected_errors = [
{'non_field_errors': [message]},
{'non_field_errors': [message]},
{'non_field_errors': [message]}
]
expected_errors = {idx: {'non_field_errors': [message]} for idx in range(len(data))}

assert serializer.errors == expected_errors

Expand Down
78 changes: 78 additions & 0 deletions tests/test_serializer_lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -775,3 +775,81 @@ def test(self):
queryset = NullableOneToOneSource.objects.all()
serializer = self.serializer(queryset, many=True)
assert serializer.data


class TestListSerializerDictErrorBehavior:
"""
Tests dict-based error structure for ListSerializer, and consistency with ListField.

https://github.com/encode/django-rest-framework/issues/7279
"""

def setup_method(self):
class SampleSerializer(serializers.Serializer):
num = serializers.BooleanField()

class ChildSerializer(serializers.Serializer):
num = serializers.BooleanField()

class WrapperSerializer(serializers.Serializer):
list_serializer = ChildSerializer(many=True)
list_field = serializers.ListField(
child=serializers.DictField(allow_empty=False)
)

self.SampleSerializer = SampleSerializer
self.WrapperSerializer = WrapperSerializer

def test_listserializer_dict_error_format(self):

data = [
{"num": "1"},
{"num": "x"},
{"num": "0"},
{"num": "hello"},
]

serializer = self.SampleSerializer(data=data, many=True)
serializer.is_valid()

errors = serializer.errors
assert isinstance(errors, dict)
for _, value in errors.items():
assert value == {
"num": [ErrorDetail(string="Must be a valid boolean.", code="invalid")]
}

def test_listserializer_and_listfield_consistency(self):

data = {
"list_serializer": [
{"num": "1"},
{"num": "wrong"},
{"num": "0"},
{"num": ""},
],
"list_field": [
{"ok": "x"},
{},
{"valid": "y"},
{},
],
}

serializer = self.WrapperSerializer(data=data)
serializer.is_valid()

errors = serializer.errors

assert isinstance(errors["list_serializer"], dict)
assert isinstance(errors["list_field"], dict)

assert set(errors["list_serializer"].keys()) == {1, 3}
assert set(errors["list_field"].keys()) == {1, 3}

assert errors["list_serializer"][1] == {
"num": [ErrorDetail(string="Must be a valid boolean.", code="invalid")]
}

for index, value in errors["list_field"].items():
assert value == [ErrorDetail(string='This dictionary may not be empty.', code='empty')]