Skip to content

Commit ee15731

Browse files
authored
Handle Django's ValidationErrors in ListField (#6423)
Without this, Django's ValidationErrors will bypass the error collection from ListField's children. Here is an example that illustrates this change. Consider a Serializer that uses ListField like this: ```python class SomeSerializer(serializers.Serializer): uuids = serializers.ListField( child=serializers.PrimaryKeyRelatedField( queryset=Model.objects.something(), validators=[SomeCustomValidator()] ) ) ``` Validating data that looks like this works fine: ```python {uuids: ['some-valid-uuid', 'some-valid-uuid']} ``` Raising a DRF ValidationError for one of the children works fine, giving an error object like: ```python {'uuids': {0: ErrorDetail(string='Some validation error')}} ``` Raising a Django ValidationError for one of the children works differently (which serializers.PrimaryKeyRelatedField can do in some cases, like when the uuid is malformed). It gives an error object like: ```python {'uuids': ["'X' is not a valid UUID."]} ``` Handling Django's ValidationErrors in ListField explicitly (like in this pull request), will maintain a regular error interface in this case: ```python {'uuids': {0: ErrorDetail(string="'X' is not a valid UUID.")}} ```
1 parent cc3c89a commit ee15731

File tree

2 files changed

+32
-0
lines changed

2 files changed

+32
-0
lines changed

rest_framework/fields.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,6 +1639,8 @@ def run_child_validation(self, data):
16391639
result.append(self.child.run_validation(item))
16401640
except ValidationError as e:
16411641
errors[idx] = e.detail
1642+
except DjangoValidationError as e:
1643+
errors[idx] = get_error_detail(e)
16421644

16431645
if not errors:
16441646
return result

tests/test_fields.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from rest_framework.fields import (
1818
BuiltinSignatureError, DjangoImageField, is_simple_callable
1919
)
20+
from tests.models import UUIDForeignKeyTarget
2021

2122
utc = datetime.timezone.utc
2223

@@ -2074,6 +2075,35 @@ class TestNestedListField(FieldValues):
20742075
field = serializers.ListField(child=serializers.ListField(child=serializers.IntegerField()))
20752076

20762077

2078+
class TestListFieldWithDjangoValidationErrors(FieldValues, TestCase):
2079+
"""
2080+
Values for `ListField` with UUIDField as child
2081+
(since UUIDField can throw ValidationErrors from Django).
2082+
The idea is to test that Django's ValidationErrors raised
2083+
from Django internals are caught and serializers in a way
2084+
that is structurally consistent with DRF's ValidationErrors.
2085+
"""
2086+
2087+
valid_inputs = []
2088+
invalid_inputs = [
2089+
(
2090+
['not-a-valid-uuid', 'd7364368-d1b3-4455-aaa3-56439b460ca2', 'some-other-invalid-uuid'],
2091+
{
2092+
0: [exceptions.ErrorDetail(string='“not-a-valid-uuid” is not a valid UUID.', code='invalid')],
2093+
1: [
2094+
exceptions.ErrorDetail(
2095+
string='Invalid pk "d7364368-d1b3-4455-aaa3-56439b460ca2" - object does not exist.',
2096+
code='does_not_exist',
2097+
)
2098+
],
2099+
2: [exceptions.ErrorDetail(string='“some-other-invalid-uuid” is not a valid UUID.', code='invalid')],
2100+
},
2101+
),
2102+
]
2103+
outputs = {}
2104+
field = serializers.ListField(child=serializers.PrimaryKeyRelatedField(queryset=UUIDForeignKeyTarget.objects.all()))
2105+
2106+
20772107
class TestEmptyListField(FieldValues):
20782108
"""
20792109
Values for `ListField` with allow_empty=False flag.

0 commit comments

Comments
 (0)