@@ -229,6 +229,35 @@ def __new__(cls, name, bases, attrs):
229229 return super (SerializerMetaclass , cls ).__new__ (cls , name , bases , attrs )
230230
231231
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+
232261@six .add_metaclass (SerializerMetaclass )
233262class Serializer (BaseSerializer ):
234263 default_error_messages = {
@@ -293,62 +322,32 @@ def run_validation(self, data=empty):
293322 performed by validators and the `.validate()` method should
294323 be coerced into an error dictionary with a 'non_fields_error' key.
295324 """
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
315328
316329 value = self .to_internal_value (data )
317330 try :
318331 self .run_validators (value )
319332 value = self .validate (value )
320333 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 ))
345336
346337 return value
347338
348339 def to_internal_value (self , data ):
349340 """
350341 Dict of native values <- Dict of primitive datatypes.
351342 """
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+
352351 ret = OrderedDict ()
353352 errors = OrderedDict ()
354353 fields = [
@@ -462,6 +461,26 @@ def get_value(self, dictionary):
462461 return html .parse_html_list (dictionary , prefix = self .field_name )
463462 return dictionary .get (self .field_name , empty )
464463
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+
465484 def to_internal_value (self , data ):
466485 """
467486 List of dicts of native values <- List of dicts of primitive datatypes.
@@ -503,6 +522,9 @@ def to_representation(self, data):
503522 self .child .to_representation (item ) for item in iterable
504523 ]
505524
525+ def validate (self , attrs ):
526+ return attrs
527+
506528 def update (self , instance , validated_data ):
507529 raise NotImplementedError (
508530 "Serializers with many=True do not support multiple update by "
0 commit comments