Skip to content

Commit 4abfa28

Browse files
authored
feat: Add some changes to ValidationError to support django style vadation errors (#8863)
1 parent 22d206c commit 4abfa28

File tree

2 files changed

+102
-3
lines changed

2 files changed

+102
-3
lines changed

rest_framework/exceptions.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,17 +144,30 @@ class ValidationError(APIException):
144144
status_code = status.HTTP_400_BAD_REQUEST
145145
default_detail = _('Invalid input.')
146146
default_code = 'invalid'
147+
default_params = {}
147148

148-
def __init__(self, detail=None, code=None):
149+
def __init__(self, detail=None, code=None, params=None):
149150
if detail is None:
150151
detail = self.default_detail
151152
if code is None:
152153
code = self.default_code
154+
if params is None:
155+
params = self.default_params
153156

154157
# For validation failures, we may collect many errors together,
155158
# so the details should always be coerced to a list if not already.
156-
if isinstance(detail, tuple):
157-
detail = list(detail)
159+
if isinstance(detail, str):
160+
detail = [detail % params]
161+
elif isinstance(detail, ValidationError):
162+
detail = detail.detail
163+
elif isinstance(detail, (list, tuple)):
164+
final_detail = []
165+
for detail_item in detail:
166+
if isinstance(detail_item, ValidationError):
167+
final_detail += detail_item.detail
168+
else:
169+
final_detail += [detail_item % params if isinstance(detail_item, str) else detail_item]
170+
detail = final_detail
158171
elif not isinstance(detail, dict) and not isinstance(detail, list):
159172
detail = [detail]
160173

tests/test_validation_error.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,89 @@ def test_validation_error_details(self):
109109
assert len(error.detail) == 2
110110
assert str(error.detail[0]) == 'message1'
111111
assert str(error.detail[1]) == 'message2'
112+
113+
114+
class TestValidationErrorWithDjangoStyle(TestCase):
115+
def test_validation_error_details(self):
116+
error = ValidationError('Invalid value: %(value)s', params={'value': '42'})
117+
assert str(error.detail[0]) == 'Invalid value: 42'
118+
119+
def test_validation_error_details_tuple(self):
120+
error = ValidationError(
121+
detail=('Invalid value: %(value1)s', 'Invalid value: %(value2)s'),
122+
params={'value1': '42', 'value2': '43'},
123+
)
124+
assert isinstance(error.detail, list)
125+
assert len(error.detail) == 2
126+
assert str(error.detail[0]) == 'Invalid value: 42'
127+
assert str(error.detail[1]) == 'Invalid value: 43'
128+
129+
def test_validation_error_details_list(self):
130+
error = ValidationError(
131+
detail=['Invalid value: %(value1)s', 'Invalid value: %(value2)s', ],
132+
params={'value1': '42', 'value2': '43'}
133+
)
134+
assert isinstance(error.detail, list)
135+
assert len(error.detail) == 2
136+
assert str(error.detail[0]) == 'Invalid value: 42'
137+
assert str(error.detail[1]) == 'Invalid value: 43'
138+
139+
def test_validation_error_details_validation_errors(self):
140+
error = ValidationError(
141+
detail=ValidationError(
142+
detail='Invalid value: %(value1)s',
143+
params={'value1': '42'},
144+
),
145+
)
146+
assert isinstance(error.detail, list)
147+
assert len(error.detail) == 1
148+
assert str(error.detail[0]) == 'Invalid value: 42'
149+
150+
def test_validation_error_details_validation_errors_list(self):
151+
error = ValidationError(
152+
detail=[
153+
ValidationError(
154+
detail='Invalid value: %(value1)s',
155+
params={'value1': '42'},
156+
),
157+
ValidationError(
158+
detail='Invalid value: %(value2)s',
159+
params={'value2': '43'},
160+
),
161+
'Invalid value: %(value3)s'
162+
],
163+
params={'value3': '44'}
164+
)
165+
assert isinstance(error.detail, list)
166+
assert len(error.detail) == 3
167+
assert str(error.detail[0]) == 'Invalid value: 42'
168+
assert str(error.detail[1]) == 'Invalid value: 43'
169+
assert str(error.detail[2]) == 'Invalid value: 44'
170+
171+
def test_validation_error_details_validation_errors_nested_list(self):
172+
error = ValidationError(
173+
detail=[
174+
ValidationError(
175+
detail='Invalid value: %(value1)s',
176+
params={'value1': '42'},
177+
),
178+
ValidationError(
179+
detail=[
180+
'Invalid value: %(value2)s',
181+
ValidationError(
182+
detail='Invalid value: %(value3)s',
183+
params={'value3': '44'},
184+
)
185+
],
186+
params={'value2': '43'},
187+
),
188+
'Invalid value: %(value4)s'
189+
],
190+
params={'value4': '45'}
191+
)
192+
assert isinstance(error.detail, list)
193+
assert len(error.detail) == 4
194+
assert str(error.detail[0]) == 'Invalid value: 42'
195+
assert str(error.detail[1]) == 'Invalid value: 43'
196+
assert str(error.detail[2]) == 'Invalid value: 44'
197+
assert str(error.detail[3]) == 'Invalid value: 45'

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy