mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-25 11:04:02 +03:00
Improve composite field child errors (#5655)
* Fixup DictField test descriptions * Nest ListField/DictField errors under the idx/key * Add nested ListField/DictField tests
This commit is contained in:
parent
22f1c59099
commit
6bd773e7f8
|
@ -1626,7 +1626,7 @@ class ListField(Field):
|
|||
self.fail('not_a_list', input_type=type(data).__name__)
|
||||
if not self.allow_empty and len(data) == 0:
|
||||
self.fail('empty')
|
||||
return [self.child.run_validation(item) for item in data]
|
||||
return self.run_child_validation(data)
|
||||
|
||||
def to_representation(self, data):
|
||||
"""
|
||||
|
@ -1634,6 +1634,20 @@ class ListField(Field):
|
|||
"""
|
||||
return [self.child.to_representation(item) if item is not None else None for item in data]
|
||||
|
||||
def run_child_validation(self, data):
|
||||
result = []
|
||||
errors = OrderedDict()
|
||||
|
||||
for idx, item in enumerate(data):
|
||||
try:
|
||||
result.append(self.child.run_validation(item))
|
||||
except ValidationError as e:
|
||||
errors[idx] = e.detail
|
||||
|
||||
if not errors:
|
||||
return result
|
||||
raise ValidationError(errors)
|
||||
|
||||
|
||||
class DictField(Field):
|
||||
child = _UnvalidatedField()
|
||||
|
@ -1669,10 +1683,7 @@ class DictField(Field):
|
|||
data = html.parse_html_dict(data)
|
||||
if not isinstance(data, dict):
|
||||
self.fail('not_a_dict', input_type=type(data).__name__)
|
||||
return {
|
||||
six.text_type(key): self.child.run_validation(value)
|
||||
for key, value in data.items()
|
||||
}
|
||||
return self.run_child_validation(data)
|
||||
|
||||
def to_representation(self, value):
|
||||
"""
|
||||
|
@ -1683,6 +1694,22 @@ class DictField(Field):
|
|||
for key, val in value.items()
|
||||
}
|
||||
|
||||
def run_child_validation(self, data):
|
||||
result = {}
|
||||
errors = OrderedDict()
|
||||
|
||||
for key, value in data.items():
|
||||
key = six.text_type(key)
|
||||
|
||||
try:
|
||||
result[key] = self.child.run_validation(value)
|
||||
except ValidationError as e:
|
||||
errors[key] = e.detail
|
||||
|
||||
if not errors:
|
||||
return result
|
||||
raise ValidationError(errors)
|
||||
|
||||
|
||||
class JSONField(Field):
|
||||
default_error_messages = {
|
||||
|
|
|
@ -1767,7 +1767,7 @@ class TestListField(FieldValues):
|
|||
]
|
||||
invalid_inputs = [
|
||||
('not a list', ['Expected a list of items but got type "str".']),
|
||||
([1, 2, 'error'], ['A valid integer is required.']),
|
||||
([1, 2, 'error', 'error'], {2: ['A valid integer is required.'], 3: ['A valid integer is required.']}),
|
||||
({'one': 'two'}, ['Expected a list of items but got type "dict".'])
|
||||
]
|
||||
outputs = [
|
||||
|
@ -1794,6 +1794,25 @@ class TestListField(FieldValues):
|
|||
assert exc_info.value.detail == ['Expected a list of items but got type "dict".']
|
||||
|
||||
|
||||
class TestNestedListField(FieldValues):
|
||||
"""
|
||||
Values for nested `ListField` with IntegerField as child.
|
||||
"""
|
||||
valid_inputs = [
|
||||
([[1, 2], [3]], [[1, 2], [3]]),
|
||||
([[]], [[]])
|
||||
]
|
||||
invalid_inputs = [
|
||||
(['not a list'], {0: ['Expected a list of items but got type "str".']}),
|
||||
([[1, 2, 'error'], ['error']], {0: {2: ['A valid integer is required.']}, 1: {0: ['A valid integer is required.']}}),
|
||||
([{'one': 'two'}], {0: ['Expected a list of items but got type "dict".']})
|
||||
]
|
||||
outputs = [
|
||||
([[1, 2], [3]], [[1, 2], [3]]),
|
||||
]
|
||||
field = serializers.ListField(child=serializers.ListField(child=serializers.IntegerField()))
|
||||
|
||||
|
||||
class TestEmptyListField(FieldValues):
|
||||
"""
|
||||
Values for `ListField` with allow_empty=False flag.
|
||||
|
@ -1834,13 +1853,13 @@ class TestUnvalidatedListField(FieldValues):
|
|||
|
||||
class TestDictField(FieldValues):
|
||||
"""
|
||||
Values for `ListField` with CharField as child.
|
||||
Values for `DictField` with CharField as child.
|
||||
"""
|
||||
valid_inputs = [
|
||||
({'a': 1, 'b': '2', 3: 3}, {'a': '1', 'b': '2', '3': '3'}),
|
||||
]
|
||||
invalid_inputs = [
|
||||
({'a': 1, 'b': None}, ['This field may not be null.']),
|
||||
({'a': 1, 'b': None, 'c': None}, {'b': ['This field may not be null.'], 'c': ['This field may not be null.']}),
|
||||
('not a dict', ['Expected a dictionary of items but got type "str".']),
|
||||
]
|
||||
outputs = [
|
||||
|
@ -1866,9 +1885,26 @@ class TestDictField(FieldValues):
|
|||
assert output is None
|
||||
|
||||
|
||||
class TestNestedDictField(FieldValues):
|
||||
"""
|
||||
Values for nested `DictField` with CharField as child.
|
||||
"""
|
||||
valid_inputs = [
|
||||
({0: {'a': 1, 'b': '2'}, 1: {3: 3}}, {'0': {'a': '1', 'b': '2'}, '1': {'3': '3'}}),
|
||||
]
|
||||
invalid_inputs = [
|
||||
({0: {'a': 1, 'b': None}, 1: {'c': None}}, {'0': {'b': ['This field may not be null.']}, '1': {'c': ['This field may not be null.']}}),
|
||||
({0: 'not a dict'}, {'0': ['Expected a dictionary of items but got type "str".']}),
|
||||
]
|
||||
outputs = [
|
||||
({0: {'a': 1, 'b': '2'}, 1: {3: 3}}, {'0': {'a': '1', 'b': '2'}, '1': {'3': '3'}}),
|
||||
]
|
||||
field = serializers.DictField(child=serializers.DictField(child=serializers.CharField()))
|
||||
|
||||
|
||||
class TestDictFieldWithNullChild(FieldValues):
|
||||
"""
|
||||
Values for `ListField` with allow_null CharField as child.
|
||||
Values for `DictField` with allow_null CharField as child.
|
||||
"""
|
||||
valid_inputs = [
|
||||
({'a': None, 'b': '2', 3: 3}, {'a': None, 'b': '2', '3': '3'}),
|
||||
|
@ -1883,7 +1919,7 @@ class TestDictFieldWithNullChild(FieldValues):
|
|||
|
||||
class TestUnvalidatedDictField(FieldValues):
|
||||
"""
|
||||
Values for `ListField` with no `child` argument.
|
||||
Values for `DictField` with no `child` argument.
|
||||
"""
|
||||
valid_inputs = [
|
||||
({'a': 1, 'b': [4, 5, 6], 1: 123}, {'a': 1, 'b': [4, 5, 6], '1': 123}),
|
||||
|
|
Loading…
Reference in New Issue
Block a user