Skip to content

Instantly share code, notes, and snippets.

@erickmendonca
Created March 27, 2018 20:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erickmendonca/96fd0d766cd3be185eefbfc81ae555ab to your computer and use it in GitHub Desktop.
Save erickmendonca/96fd0d766cd3be185eefbfc81ae555ab to your computer and use it in GitHub Desktop.
Serializer - throw all errors at the end of validation
from typing import Mapping
from collections import OrderedDict
from django.core.exceptions import ValidationError as DjangoValidationError
from rest_framework import serializers
from rest_framework.fields import empty
class AllErrorsSerializer(serializers.Serializer):
'''
This serializer avoids erroring out sooner on data missing
required fields or failing on validators defined on the field
declaration.
Warning: required fields might not be present on custom `.validate()`
methods since this class does not raise an ValidationError earlier.
'''
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._errors = dict()
def to_internal_value(self, data):
''''
Overrides Serializer `to_internal_value` to store field validation
errors instead of raising an Exception right away. These errors
will be thrown out at the end of `run_validation` along with other
custom validations.
'''
if not isinstance(data, Mapping):
message = self.error_messages['invalid'].format(
datatype=type(data).__name__
)
raise serializers.ValidationError({
serializers.api_settings.NON_FIELD_ERRORS_KEY: [message]
}, code='invalid')
ret = OrderedDict()
errors = OrderedDict()
fields = self._writable_fields
for field in fields:
validate_method = getattr(
self, 'validate_' + field.field_name, None,
)
primitive_value = field.get_value(data)
try:
validated_value = field.run_validation(primitive_value)
if validate_method is not None:
validated_value = validate_method(validated_value)
except serializers.ValidationError as exc:
errors[field.field_name] = exc.detail
except DjangoValidationError as exc:
errors[field.field_name] = serializers.get_error_detail(exc)
except serializers.SkipField:
pass
else:
serializers.set_value(ret, field.source_attrs, validated_value)
# save errors instead of raising an Exception
if errors:
self._errors.update(errors)
return ret
def run_validation(self, data=empty):
'''
We override this method so we can coerce all errors on a single
ValidationError.
'''
(is_empty_value, data) = self.validate_empty_values(data)
if is_empty_value:
return data
value = self.to_internal_value(data)
errors = list()
try:
self.run_validators(value)
except (serializers.ValidationError, DjangoValidationError) as exc:
errors.extend(exc.detail)
try:
value = self.validate(value)
except (serializers.ValidationError, DjangoValidationError) as exc:
errors.extend(exc.detail)
assert value is not None, (
'.validate() should return the validated data')
if errors or self.errors:
raise serializers.ValidationError(
{**self.errors, 'non_field_errors': errors},
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment