mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-25 19:14:01 +03:00
Added ListSerializer.validate(). Closes #2168.
This commit is contained in:
parent
ef89c15663
commit
eee02a47d9
|
@ -294,6 +294,34 @@ class Field(object):
|
|||
return self.default()
|
||||
return self.default
|
||||
|
||||
def validate_empty_values(self, data):
|
||||
"""
|
||||
Validate empty values, and either:
|
||||
|
||||
* Raise `ValidationError`, indicating invalid data.
|
||||
* Raise `SkipField`, indicating that the field should be ignored.
|
||||
* Return (True, data), indicating an empty value that should be
|
||||
returned without any furhter validation being applied.
|
||||
* Return (False, data), indicating a non-empty value, that should
|
||||
have validation applied as normal.
|
||||
"""
|
||||
if self.read_only:
|
||||
return (True, self.get_default())
|
||||
|
||||
if data is empty:
|
||||
if getattr(self.root, 'partial', False):
|
||||
raise SkipField()
|
||||
if self.required:
|
||||
self.fail('required')
|
||||
return (True, self.get_default())
|
||||
|
||||
if data is None:
|
||||
if not self.allow_null:
|
||||
self.fail('null')
|
||||
return (True, None)
|
||||
|
||||
return (False, data)
|
||||
|
||||
def run_validation(self, data=empty):
|
||||
"""
|
||||
Validate a simple representation and return the internal value.
|
||||
|
@ -304,21 +332,9 @@ class Field(object):
|
|||
May raise `SkipField` if the field should not be included in the
|
||||
validated data.
|
||||
"""
|
||||
if self.read_only:
|
||||
return self.get_default()
|
||||
|
||||
if data is empty:
|
||||
if getattr(self.root, 'partial', False):
|
||||
raise SkipField()
|
||||
if self.required:
|
||||
self.fail('required')
|
||||
return self.get_default()
|
||||
|
||||
if data is None:
|
||||
if not self.allow_null:
|
||||
self.fail('null')
|
||||
return None
|
||||
|
||||
(is_empty_value, data) = self.validate_empty_values(data)
|
||||
if is_empty_value:
|
||||
return data
|
||||
value = self.to_internal_value(data)
|
||||
self.run_validators(value)
|
||||
return value
|
||||
|
|
|
@ -229,6 +229,35 @@ class SerializerMetaclass(type):
|
|||
return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs)
|
||||
|
||||
|
||||
def get_validation_error_detail(exc):
|
||||
assert isinstance(exc, (ValidationError, DjangoValidationError))
|
||||
|
||||
if isinstance(exc, DjangoValidationError):
|
||||
# Normally you should raise `serializers.ValidationError`
|
||||
# inside your codebase, but we handle Django's validation
|
||||
# exception class as well for simpler compat.
|
||||
# Eg. Calling Model.clean() explicitly inside Serializer.validate()
|
||||
return {
|
||||
api_settings.NON_FIELD_ERRORS_KEY: list(exc.messages)
|
||||
}
|
||||
elif isinstance(exc.detail, dict):
|
||||
# If errors may be a dict we use the standard {key: list of values}.
|
||||
# Here we ensure that all the values are *lists* of errors.
|
||||
return dict([
|
||||
(key, value if isinstance(value, list) else [value])
|
||||
for key, value in exc.detail.items()
|
||||
])
|
||||
elif isinstance(exc.detail, list):
|
||||
# Errors raised as a list are non-field errors.
|
||||
return {
|
||||
api_settings.NON_FIELD_ERRORS_KEY: exc.detail
|
||||
}
|
||||
# Errors raised as a string are non-field errors.
|
||||
return {
|
||||
api_settings.NON_FIELD_ERRORS_KEY: [exc.detail]
|
||||
}
|
||||
|
||||
|
||||
@six.add_metaclass(SerializerMetaclass)
|
||||
class Serializer(BaseSerializer):
|
||||
default_error_messages = {
|
||||
|
@ -293,18 +322,24 @@ class Serializer(BaseSerializer):
|
|||
performed by validators and the `.validate()` method should
|
||||
be coerced into an error dictionary with a 'non_fields_error' key.
|
||||
"""
|
||||
if data is empty:
|
||||
if getattr(self.root, 'partial', False):
|
||||
raise SkipField()
|
||||
if self.required:
|
||||
self.fail('required')
|
||||
return self.get_default()
|
||||
(is_empty_value, data) = self.validate_empty_values(data)
|
||||
if is_empty_value:
|
||||
return data
|
||||
|
||||
if data is None:
|
||||
if not self.allow_null:
|
||||
self.fail('null')
|
||||
return None
|
||||
value = self.to_internal_value(data)
|
||||
try:
|
||||
self.run_validators(value)
|
||||
value = self.validate(value)
|
||||
assert value is not None, '.validate() should return the validated data'
|
||||
except (ValidationError, DjangoValidationError) as exc:
|
||||
raise ValidationError(detail=get_validation_error_detail(exc))
|
||||
|
||||
return value
|
||||
|
||||
def to_internal_value(self, data):
|
||||
"""
|
||||
Dict of native values <- Dict of primitive datatypes.
|
||||
"""
|
||||
if not isinstance(data, dict):
|
||||
message = self.error_messages['invalid'].format(
|
||||
datatype=type(data).__name__
|
||||
|
@ -313,42 +348,6 @@ class Serializer(BaseSerializer):
|
|||
api_settings.NON_FIELD_ERRORS_KEY: [message]
|
||||
})
|
||||
|
||||
value = self.to_internal_value(data)
|
||||
try:
|
||||
self.run_validators(value)
|
||||
value = self.validate(value)
|
||||
assert value is not None, '.validate() should return the validated data'
|
||||
except ValidationError as exc:
|
||||
if isinstance(exc.detail, dict):
|
||||
# .validate() errors may be a dict, in which case, use
|
||||
# standard {key: list of values} style.
|
||||
raise ValidationError(dict([
|
||||
(key, value if isinstance(value, list) else [value])
|
||||
for key, value in exc.detail.items()
|
||||
]))
|
||||
elif isinstance(exc.detail, list):
|
||||
raise ValidationError({
|
||||
api_settings.NON_FIELD_ERRORS_KEY: exc.detail
|
||||
})
|
||||
else:
|
||||
raise ValidationError({
|
||||
api_settings.NON_FIELD_ERRORS_KEY: [exc.detail]
|
||||
})
|
||||
except DjangoValidationError as exc:
|
||||
# Normally you should raise `serializers.ValidationError`
|
||||
# inside your codebase, but we handle Django's validation
|
||||
# exception class as well for simpler compat.
|
||||
# Eg. Calling Model.clean() explicitly inside Serializer.validate()
|
||||
raise ValidationError({
|
||||
api_settings.NON_FIELD_ERRORS_KEY: list(exc.messages)
|
||||
})
|
||||
|
||||
return value
|
||||
|
||||
def to_internal_value(self, data):
|
||||
"""
|
||||
Dict of native values <- Dict of primitive datatypes.
|
||||
"""
|
||||
ret = OrderedDict()
|
||||
errors = OrderedDict()
|
||||
fields = [
|
||||
|
@ -462,6 +461,26 @@ class ListSerializer(BaseSerializer):
|
|||
return html.parse_html_list(dictionary, prefix=self.field_name)
|
||||
return dictionary.get(self.field_name, empty)
|
||||
|
||||
def run_validation(self, data=empty):
|
||||
"""
|
||||
We override the default `run_validation`, because the validation
|
||||
performed by validators and the `.validate()` method should
|
||||
be coerced into an error dictionary with a 'non_fields_error' key.
|
||||
"""
|
||||
(is_empty_value, data) = self.validate_empty_values(data)
|
||||
if is_empty_value:
|
||||
return data
|
||||
|
||||
value = self.to_internal_value(data)
|
||||
try:
|
||||
self.run_validators(value)
|
||||
value = self.validate(value)
|
||||
assert value is not None, '.validate() should return the validated data'
|
||||
except (ValidationError, DjangoValidationError) as exc:
|
||||
raise ValidationError(detail=get_validation_error_detail(exc))
|
||||
|
||||
return value
|
||||
|
||||
def to_internal_value(self, data):
|
||||
"""
|
||||
List of dicts of native values <- List of dicts of primitive datatypes.
|
||||
|
@ -503,6 +522,9 @@ class ListSerializer(BaseSerializer):
|
|||
self.child.to_representation(item) for item in iterable
|
||||
]
|
||||
|
||||
def validate(self, attrs):
|
||||
return attrs
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
raise NotImplementedError(
|
||||
"Serializers with many=True do not support multiple update by "
|
||||
|
|
|
@ -272,3 +272,19 @@ class TestNestedListOfListsSerializer:
|
|||
serializer = self.Serializer(data=input_data)
|
||||
assert serializer.is_valid()
|
||||
assert serializer.validated_data == expected_output
|
||||
|
||||
|
||||
class TestListSerializerClass:
|
||||
"""Tests for a custom list_serializer_class."""
|
||||
def test_list_serializer_class_validate(self):
|
||||
class CustomListSerializer(serializers.ListSerializer):
|
||||
def validate(self, attrs):
|
||||
raise serializers.ValidationError('Non field error')
|
||||
|
||||
class TestSerializer(serializers.Serializer):
|
||||
class Meta:
|
||||
list_serializer_class = CustomListSerializer
|
||||
|
||||
serializer = TestSerializer(data=[], many=True)
|
||||
assert not serializer.is_valid()
|
||||
assert serializer.errors == {'non_field_errors': ['Non field error']}
|
||||
|
|
Loading…
Reference in New Issue
Block a user