@@ -229,6 +229,35 @@ def __new__(cls, name, bases, attrs):
229
229
return super (SerializerMetaclass , cls ).__new__ (cls , name , bases , attrs )
230
230
231
231
232
+ def get_validation_error_detail (exc ):
233
+ assert isinstance (exc , (ValidationError , DjangoValidationError ))
234
+
235
+ if isinstance (exc , DjangoValidationError ):
236
+ # Normally you should raise `serializers.ValidationError`
237
+ # inside your codebase, but we handle Django's validation
238
+ # exception class as well for simpler compat.
239
+ # Eg. Calling Model.clean() explicitly inside Serializer.validate()
240
+ return {
241
+ api_settings .NON_FIELD_ERRORS_KEY : list (exc .messages )
242
+ }
243
+ elif isinstance (exc .detail , dict ):
244
+ # If errors may be a dict we use the standard {key: list of values}.
245
+ # Here we ensure that all the values are *lists* of errors.
246
+ return dict ([
247
+ (key , value if isinstance (value , list ) else [value ])
248
+ for key , value in exc .detail .items ()
249
+ ])
250
+ elif isinstance (exc .detail , list ):
251
+ # Errors raised as a list are non-field errors.
252
+ return {
253
+ api_settings .NON_FIELD_ERRORS_KEY : exc .detail
254
+ }
255
+ # Errors raised as a string are non-field errors.
256
+ return {
257
+ api_settings .NON_FIELD_ERRORS_KEY : [exc .detail ]
258
+ }
259
+
260
+
232
261
@six .add_metaclass (SerializerMetaclass )
233
262
class Serializer (BaseSerializer ):
234
263
default_error_messages = {
@@ -293,62 +322,32 @@ def run_validation(self, data=empty):
293
322
performed by validators and the `.validate()` method should
294
323
be coerced into an error dictionary with a 'non_fields_error' key.
295
324
"""
296
- if data is empty :
297
- if getattr (self .root , 'partial' , False ):
298
- raise SkipField ()
299
- if self .required :
300
- self .fail ('required' )
301
- return self .get_default ()
302
-
303
- if data is None :
304
- if not self .allow_null :
305
- self .fail ('null' )
306
- return None
307
-
308
- if not isinstance (data , dict ):
309
- message = self .error_messages ['invalid' ].format (
310
- datatype = type (data ).__name__
311
- )
312
- raise ValidationError ({
313
- api_settings .NON_FIELD_ERRORS_KEY : [message ]
314
- })
325
+ (is_empty_value , data ) = self .validate_empty_values (data )
326
+ if is_empty_value :
327
+ return data
315
328
316
329
value = self .to_internal_value (data )
317
330
try :
318
331
self .run_validators (value )
319
332
value = self .validate (value )
320
333
assert value is not None , '.validate() should return the validated data'
321
- except ValidationError as exc :
322
- if isinstance (exc .detail , dict ):
323
- # .validate() errors may be a dict, in which case, use
324
- # standard {key: list of values} style.
325
- raise ValidationError (dict ([
326
- (key , value if isinstance (value , list ) else [value ])
327
- for key , value in exc .detail .items ()
328
- ]))
329
- elif isinstance (exc .detail , list ):
330
- raise ValidationError ({
331
- api_settings .NON_FIELD_ERRORS_KEY : exc .detail
332
- })
333
- else :
334
- raise ValidationError ({
335
- api_settings .NON_FIELD_ERRORS_KEY : [exc .detail ]
336
- })
337
- except DjangoValidationError as exc :
338
- # Normally you should raise `serializers.ValidationError`
339
- # inside your codebase, but we handle Django's validation
340
- # exception class as well for simpler compat.
341
- # Eg. Calling Model.clean() explicitly inside Serializer.validate()
342
- raise ValidationError ({
343
- api_settings .NON_FIELD_ERRORS_KEY : list (exc .messages )
344
- })
334
+ except (ValidationError , DjangoValidationError ) as exc :
335
+ raise ValidationError (detail = get_validation_error_detail (exc ))
345
336
346
337
return value
347
338
348
339
def to_internal_value (self , data ):
349
340
"""
350
341
Dict of native values <- Dict of primitive datatypes.
351
342
"""
343
+ if not isinstance (data , dict ):
344
+ message = self .error_messages ['invalid' ].format (
345
+ datatype = type (data ).__name__
346
+ )
347
+ raise ValidationError ({
348
+ api_settings .NON_FIELD_ERRORS_KEY : [message ]
349
+ })
350
+
352
351
ret = OrderedDict ()
353
352
errors = OrderedDict ()
354
353
fields = [
@@ -462,6 +461,26 @@ def get_value(self, dictionary):
462
461
return html .parse_html_list (dictionary , prefix = self .field_name )
463
462
return dictionary .get (self .field_name , empty )
464
463
464
+ def run_validation (self , data = empty ):
465
+ """
466
+ We override the default `run_validation`, because the validation
467
+ performed by validators and the `.validate()` method should
468
+ be coerced into an error dictionary with a 'non_fields_error' key.
469
+ """
470
+ (is_empty_value , data ) = self .validate_empty_values (data )
471
+ if is_empty_value :
472
+ return data
473
+
474
+ value = self .to_internal_value (data )
475
+ try :
476
+ self .run_validators (value )
477
+ value = self .validate (value )
478
+ assert value is not None , '.validate() should return the validated data'
479
+ except (ValidationError , DjangoValidationError ) as exc :
480
+ raise ValidationError (detail = get_validation_error_detail (exc ))
481
+
482
+ return value
483
+
465
484
def to_internal_value (self , data ):
466
485
"""
467
486
List of dicts of native values <- List of dicts of primitive datatypes.
@@ -503,6 +522,9 @@ def to_representation(self, data):
503
522
self .child .to_representation (item ) for item in iterable
504
523
]
505
524
525
+ def validate (self , attrs ):
526
+ return attrs
527
+
506
528
def update (self , instance , validated_data ):
507
529
raise NotImplementedError (
508
530
"Serializers with many=True do not support multiple update by "
0 commit comments