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
9 changes: 4 additions & 5 deletions rest_framework/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,18 +688,17 @@ def to_internal_value(self, data):
}, code='min_length')

ret = []
errors = []
errors = {}

for item in data:
for index, item in enumerate(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({})

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
77 changes: 77 additions & 0 deletions tests/test_serializer_lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -775,3 +775,80 @@ 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)
assert set(errors.keys()) == {1, 3}

assert errors[1] == {"num": [ErrorDetail(string="Must be a valid boolean.", code="invalid")]}
assert errors[3] == {"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")]}
assert errors["list_serializer"][3] == {"num": [ErrorDetail(string="Must be a valid boolean.", code="invalid")]}

assert errors["list_field"][1] == [ErrorDetail(string='This dictionary may not be empty.', code='empty')]
assert errors["list_field"][3] == [ErrorDetail(string='This dictionary may not be empty.', code='empty')]