Skip to content

Commit 6b66005

Browse files
committed
Handle Django's ValidationErrors in ListField
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 6b66005

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)