Use serializers.ValidationError

This commit is contained in:
Tom Christie 2014-10-17 13:23:14 +01:00
parent 5882a7a9d5
commit 05cbec9dd7
8 changed files with 120 additions and 107 deletions

View File

@ -144,11 +144,15 @@ The corresponding code would now look like this:
logging.info('Creating ticket "%s"' % name) logging.info('Creating ticket "%s"' % name)
serializer.save(user=request.user) # Include the user when saving. serializer.save(user=request.user) # Include the user when saving.
#### Use `rest_framework.exceptions.ValidationFailed`. #### Using `serializers.ValidationError`.
Django's `ValidationError` class is intended for use with HTML forms and it's API makes its use slightly awkward with nested validation errors as can occur in serializers. Previously `serializers.ValidationError` error was simply a synonym for `django.core.exceptions.ValidationError`. This has now been altered so that it inherits from the standard `APIException` base class.
We now include a simpler `ValidationFailed` exception class in REST framework that you should use when raising validation failures. The reason behind this is that Django's `ValidationError` class is intended for use with HTML forms and its API makes using it slightly awkward with nested validation errors that can occur in serializers.
For most users this change shouldn't require any updates to your codebase, but it is worth ensuring that whenever raising validation errors you are always using the `serializers.ValidationError` exception class, and not Django's built-in exception.
We strongly recommend that you use the namespaced import style of `import serializers` and not `from serializers import ValidationError` in order to avoid any potential confusion.
#### Change to `validate_<field_name>`. #### Change to `validate_<field_name>`.
@ -156,14 +160,14 @@ The `validate_<field_name>` method hooks that can be attached to serializer clas
def validate_score(self, attrs, source): def validate_score(self, attrs, source):
if attrs[score] % 10 != 0: if attrs[score] % 10 != 0:
raise ValidationError('This field should be a multiple of ten.') raise serializers.ValidationError('This field should be a multiple of ten.')
return attrs return attrs
This is now simplified slightly, and the method hooks simply take the value to be validated, and return it's validated value. This is now simplified slightly, and the method hooks simply take the value to be validated, and return it's validated value.
def validate_score(self, value): def validate_score(self, value):
if value % 10 != 0: if value % 10 != 0:
raise ValidationError('This field should be a multiple of ten.') raise serializers.ValidationError('This field should be a multiple of ten.')
return value return value
Any ad-hoc validation that applies to more than one field should go in the `.validate(self, attrs)` method as usual. Any ad-hoc validation that applies to more than one field should go in the `.validate(self, attrs)` method as usual.

View File

@ -18,13 +18,13 @@ class AuthTokenSerializer(serializers.Serializer):
if user: if user:
if not user.is_active: if not user.is_active:
msg = _('User account is disabled.') msg = _('User account is disabled.')
raise exceptions.ValidationFailed(msg) raise exceptions.ValidationError(msg)
else: else:
msg = _('Unable to log in with provided credentials.') msg = _('Unable to log in with provided credentials.')
raise exceptions.ValidationFailed(msg) raise exceptions.ValidationError(msg)
else: else:
msg = _('Must include "username" and "password"') msg = _('Must include "username" and "password"')
raise exceptions.ValidationFailed(msg) raise exceptions.ValidationError(msg)
attrs['user'] = user attrs['user'] = user
return attrs return attrs

View File

@ -24,7 +24,14 @@ class APIException(Exception):
return self.detail return self.detail
class ValidationFailed(APIException): # The recommended style for using `ValidationError` is to keep it namespaced
# under `serializers`, in order to minimize potential confusion with Django's
# built in `ValidationError`. For example:
#
# from rest_framework import serializers
# raise serializers.ValidationError('Value was invalid')
class ValidationError(APIException):
status_code = status.HTTP_400_BAD_REQUEST status_code = status.HTTP_400_BAD_REQUEST
def __init__(self, detail): def __init__(self, detail):

View File

@ -13,7 +13,7 @@ from rest_framework.compat import (
smart_text, EmailValidator, MinValueValidator, MaxValueValidator, smart_text, EmailValidator, MinValueValidator, MaxValueValidator,
MinLengthValidator, MaxLengthValidator, URLValidator MinLengthValidator, MaxLengthValidator, URLValidator
) )
from rest_framework.exceptions import ValidationFailed from rest_framework.exceptions import ValidationError
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from rest_framework.utils import html, representation, humanize_datetime from rest_framework.utils import html, representation, humanize_datetime
import copy import copy
@ -102,7 +102,7 @@ NOT_READ_ONLY_DEFAULT = 'May not set both `read_only` and `default`'
NOT_REQUIRED_DEFAULT = 'May not set both `required` and `default`' NOT_REQUIRED_DEFAULT = 'May not set both `required` and `default`'
USE_READONLYFIELD = 'Field(read_only=True) should be ReadOnlyField' USE_READONLYFIELD = 'Field(read_only=True) should be ReadOnlyField'
MISSING_ERROR_MESSAGE = ( MISSING_ERROR_MESSAGE = (
'ValidationFailed raised by `{class_name}`, but error key `{key}` does ' 'ValidationError raised by `{class_name}`, but error key `{key}` does '
'not exist in the `error_messages` dictionary.' 'not exist in the `error_messages` dictionary.'
) )
@ -264,7 +264,7 @@ class Field(object):
def run_validators(self, value): def run_validators(self, value):
""" """
Test the given value against all the validators on the field, Test the given value against all the validators on the field,
and either raise a `ValidationFailed` or simply return. and either raise a `ValidationError` or simply return.
""" """
errors = [] errors = []
for validator in self.validators: for validator in self.validators:
@ -272,12 +272,12 @@ class Field(object):
validator.serializer_field = self validator.serializer_field = self
try: try:
validator(value) validator(value)
except ValidationFailed as exc: except ValidationError as exc:
errors.extend(exc.detail) errors.extend(exc.detail)
except DjangoValidationError as exc: except DjangoValidationError as exc:
errors.extend(exc.messages) errors.extend(exc.messages)
if errors: if errors:
raise ValidationFailed(errors) raise ValidationError(errors)
def validate(self, value): def validate(self, value):
pass pass
@ -305,7 +305,7 @@ class Field(object):
msg = MISSING_ERROR_MESSAGE.format(class_name=class_name, key=key) msg = MISSING_ERROR_MESSAGE.format(class_name=class_name, key=key)
raise AssertionError(msg) raise AssertionError(msg)
message_string = msg.format(**kwargs) message_string = msg.format(**kwargs)
raise ValidationFailed(message_string) raise ValidationError(message_string)
@property @property
def root(self): def root(self):

View File

@ -14,7 +14,7 @@ from django.core.exceptions import ImproperlyConfigured
from django.db import models from django.db import models
from django.utils import six from django.utils import six
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from rest_framework.exceptions import ValidationFailed from rest_framework.exceptions import ValidationError
from rest_framework.fields import empty, set_value, Field, SkipField from rest_framework.fields import empty, set_value, Field, SkipField
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from rest_framework.utils import html, model_meta, representation from rest_framework.utils import html, model_meta, representation
@ -77,13 +77,6 @@ class BaseSerializer(Field):
raise NotImplementedError('`create()` must be implemented.') raise NotImplementedError('`create()` must be implemented.')
def save(self, **kwargs): def save(self, **kwargs):
assert not hasattr(self, 'restore_object'), (
'Serializer %s has old-style version 2 `.restore_object()` '
'that is no longer compatible with REST framework 3. '
'Use the new-style `.create()` and `.update()` methods instead.' %
self.__class__.__name__
)
validated_data = self.validated_data validated_data = self.validated_data
if kwargs: if kwargs:
validated_data = dict( validated_data = dict(
@ -105,17 +98,24 @@ class BaseSerializer(Field):
return self.instance return self.instance
def is_valid(self, raise_exception=False): def is_valid(self, raise_exception=False):
assert not hasattr(self, 'restore_object'), (
'Serializer %s has old-style version 2 `.restore_object()` '
'that is no longer compatible with REST framework 3. '
'Use the new-style `.create()` and `.update()` methods instead.' %
self.__class__.__name__
)
if not hasattr(self, '_validated_data'): if not hasattr(self, '_validated_data'):
try: try:
self._validated_data = self.run_validation(self._initial_data) self._validated_data = self.run_validation(self._initial_data)
except ValidationFailed as exc: except ValidationError as exc:
self._validated_data = {} self._validated_data = {}
self._errors = exc.detail self._errors = exc.detail
else: else:
self._errors = {} self._errors = {}
if self._errors and raise_exception: if self._errors and raise_exception:
raise ValidationFailed(self._errors) raise ValidationError(self._errors)
return not bool(self._errors) return not bool(self._errors)
@ -124,6 +124,8 @@ class BaseSerializer(Field):
if not hasattr(self, '_data'): if not hasattr(self, '_data'):
if self.instance is not None and not getattr(self, '_errors', None): if self.instance is not None and not getattr(self, '_errors', None):
self._data = self.to_representation(self.instance) self._data = self.to_representation(self.instance)
elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):
self._data = self.to_representation(self.validated_data)
else: else:
self._data = self.get_initial() self._data = self.get_initial()
return self._data return self._data
@ -329,7 +331,7 @@ class Serializer(BaseSerializer):
return None return None
if not isinstance(data, dict): if not isinstance(data, dict):
raise ValidationFailed({ raise ValidationError({
api_settings.NON_FIELD_ERRORS_KEY: ['Invalid data'] api_settings.NON_FIELD_ERRORS_KEY: ['Invalid data']
}) })
@ -338,8 +340,8 @@ class Serializer(BaseSerializer):
self.run_validators(value) self.run_validators(value)
value = self.validate(value) value = self.validate(value)
assert value is not None, '.validate() should return the validated data' assert value is not None, '.validate() should return the validated data'
except ValidationFailed as exc: except ValidationError as exc:
raise ValidationFailed({ raise ValidationError({
api_settings.NON_FIELD_ERRORS_KEY: exc.detail api_settings.NON_FIELD_ERRORS_KEY: exc.detail
}) })
return value return value
@ -359,7 +361,7 @@ class Serializer(BaseSerializer):
validated_value = field.run_validation(primitive_value) validated_value = field.run_validation(primitive_value)
if validate_method is not None: if validate_method is not None:
validated_value = validate_method(validated_value) validated_value = validate_method(validated_value)
except ValidationFailed as exc: except ValidationError as exc:
errors[field.field_name] = exc.detail errors[field.field_name] = exc.detail
except SkipField: except SkipField:
pass pass
@ -367,7 +369,7 @@ class Serializer(BaseSerializer):
set_value(ret, field.source_attrs, validated_value) set_value(ret, field.source_attrs, validated_value)
if errors: if errors:
raise ValidationFailed(errors) raise ValidationError(errors)
return ret return ret

View File

@ -1,6 +1,6 @@
from decimal import Decimal from decimal import Decimal
from django.utils import timezone from django.utils import timezone
from rest_framework import exceptions, fields, serializers from rest_framework import serializers
import datetime import datetime
import django import django
import pytest import pytest
@ -17,8 +17,8 @@ class TestEmpty:
""" """
By default a field must be included in the input. By default a field must be included in the input.
""" """
field = fields.IntegerField() field = serializers.IntegerField()
with pytest.raises(exceptions.ValidationFailed) as exc_info: with pytest.raises(serializers.ValidationError) as exc_info:
field.run_validation() field.run_validation()
assert exc_info.value.detail == ['This field is required.'] assert exc_info.value.detail == ['This field is required.']
@ -26,16 +26,16 @@ class TestEmpty:
""" """
If `required=False` then a field may be omitted from the input. If `required=False` then a field may be omitted from the input.
""" """
field = fields.IntegerField(required=False) field = serializers.IntegerField(required=False)
with pytest.raises(fields.SkipField): with pytest.raises(serializers.SkipField):
field.run_validation() field.run_validation()
def test_disallow_null(self): def test_disallow_null(self):
""" """
By default `None` is not a valid input. By default `None` is not a valid input.
""" """
field = fields.IntegerField() field = serializers.IntegerField()
with pytest.raises(exceptions.ValidationFailed) as exc_info: with pytest.raises(serializers.ValidationError) as exc_info:
field.run_validation(None) field.run_validation(None)
assert exc_info.value.detail == ['This field may not be null.'] assert exc_info.value.detail == ['This field may not be null.']
@ -43,7 +43,7 @@ class TestEmpty:
""" """
If `allow_null=True` then `None` is a valid input. If `allow_null=True` then `None` is a valid input.
""" """
field = fields.IntegerField(allow_null=True) field = serializers.IntegerField(allow_null=True)
output = field.run_validation(None) output = field.run_validation(None)
assert output is None assert output is None
@ -51,8 +51,8 @@ class TestEmpty:
""" """
By default '' is not a valid input. By default '' is not a valid input.
""" """
field = fields.CharField() field = serializers.CharField()
with pytest.raises(exceptions.ValidationFailed) as exc_info: with pytest.raises(serializers.ValidationError) as exc_info:
field.run_validation('') field.run_validation('')
assert exc_info.value.detail == ['This field may not be blank.'] assert exc_info.value.detail == ['This field may not be blank.']
@ -60,7 +60,7 @@ class TestEmpty:
""" """
If `allow_blank=True` then '' is a valid input. If `allow_blank=True` then '' is a valid input.
""" """
field = fields.CharField(allow_blank=True) field = serializers.CharField(allow_blank=True)
output = field.run_validation('') output = field.run_validation('')
assert output is '' assert output is ''
@ -68,7 +68,7 @@ class TestEmpty:
""" """
If `default` is set, then omitted values get the default input. If `default` is set, then omitted values get the default input.
""" """
field = fields.IntegerField(default=123) field = serializers.IntegerField(default=123)
output = field.run_validation() output = field.run_validation()
assert output is 123 assert output is 123
@ -96,13 +96,13 @@ class TestSource:
class TestReadOnly: class TestReadOnly:
def setup(self): def setup(self):
class TestSerializer(serializers.Serializer): class TestSerializer(serializers.Serializer):
read_only = fields.ReadOnlyField() read_only = serializers.ReadOnlyField()
writable = fields.IntegerField() writable = serializers.IntegerField()
self.Serializer = TestSerializer self.Serializer = TestSerializer
def test_validate_read_only(self): def test_validate_read_only(self):
""" """
Read-only fields should not be included in validation. Read-only serializers.should not be included in validation.
""" """
data = {'read_only': 123, 'writable': 456} data = {'read_only': 123, 'writable': 456}
serializer = self.Serializer(data=data) serializer = self.Serializer(data=data)
@ -111,7 +111,7 @@ class TestReadOnly:
def test_serialize_read_only(self): def test_serialize_read_only(self):
""" """
Read-only fields should be serialized. Read-only serializers.should be serialized.
""" """
instance = {'read_only': 123, 'writable': 456} instance = {'read_only': 123, 'writable': 456}
serializer = self.Serializer(instance) serializer = self.Serializer(instance)
@ -121,13 +121,13 @@ class TestReadOnly:
class TestWriteOnly: class TestWriteOnly:
def setup(self): def setup(self):
class TestSerializer(serializers.Serializer): class TestSerializer(serializers.Serializer):
write_only = fields.IntegerField(write_only=True) write_only = serializers.IntegerField(write_only=True)
readable = fields.IntegerField() readable = serializers.IntegerField()
self.Serializer = TestSerializer self.Serializer = TestSerializer
def test_validate_write_only(self): def test_validate_write_only(self):
""" """
Write-only fields should be included in validation. Write-only serializers.should be included in validation.
""" """
data = {'write_only': 123, 'readable': 456} data = {'write_only': 123, 'readable': 456}
serializer = self.Serializer(data=data) serializer = self.Serializer(data=data)
@ -136,7 +136,7 @@ class TestWriteOnly:
def test_serialize_write_only(self): def test_serialize_write_only(self):
""" """
Write-only fields should not be serialized. Write-only serializers.should not be serialized.
""" """
instance = {'write_only': 123, 'readable': 456} instance = {'write_only': 123, 'readable': 456}
serializer = self.Serializer(instance) serializer = self.Serializer(instance)
@ -146,8 +146,8 @@ class TestWriteOnly:
class TestInitial: class TestInitial:
def setup(self): def setup(self):
class TestSerializer(serializers.Serializer): class TestSerializer(serializers.Serializer):
initial_field = fields.IntegerField(initial=123) initial_field = serializers.IntegerField(initial=123)
blank_field = fields.IntegerField() blank_field = serializers.IntegerField()
self.serializer = TestSerializer() self.serializer = TestSerializer()
def test_initial(self): def test_initial(self):
@ -163,7 +163,7 @@ class TestInitial:
class TestLabel: class TestLabel:
def setup(self): def setup(self):
class TestSerializer(serializers.Serializer): class TestSerializer(serializers.Serializer):
labeled = fields.IntegerField(label='My label') labeled = serializers.IntegerField(label='My label')
self.serializer = TestSerializer() self.serializer = TestSerializer()
def test_label(self): def test_label(self):
@ -189,7 +189,7 @@ class TestInvalidErrorKey:
with pytest.raises(AssertionError) as exc_info: with pytest.raises(AssertionError) as exc_info:
self.field.to_native(123) self.field.to_native(123)
expected = ( expected = (
'ValidationFailed raised by `ExampleField`, but error key ' 'ValidationError raised by `ExampleField`, but error key '
'`incorrect` does not exist in the `error_messages` dictionary.' '`incorrect` does not exist in the `error_messages` dictionary.'
) )
assert str(exc_info.value) == expected assert str(exc_info.value) == expected
@ -198,7 +198,7 @@ class TestInvalidErrorKey:
class TestBooleanHTMLInput: class TestBooleanHTMLInput:
def setup(self): def setup(self):
class TestSerializer(serializers.Serializer): class TestSerializer(serializers.Serializer):
archived = fields.BooleanField() archived = serializers.BooleanField()
self.Serializer = TestSerializer self.Serializer = TestSerializer
def test_empty_html_checkbox(self): def test_empty_html_checkbox(self):
@ -243,7 +243,7 @@ class FieldValues:
Ensure that invalid values raise the expected validation error. Ensure that invalid values raise the expected validation error.
""" """
for input_value, expected_failure in get_items(self.invalid_inputs): for input_value, expected_failure in get_items(self.invalid_inputs):
with pytest.raises(exceptions.ValidationFailed) as exc_info: with pytest.raises(serializers.ValidationError) as exc_info:
self.field.run_validation(input_value) self.field.run_validation(input_value)
assert exc_info.value.detail == expected_failure assert exc_info.value.detail == expected_failure
@ -283,7 +283,7 @@ class TestBooleanField(FieldValues):
False: False, False: False,
'other': True 'other': True
} }
field = fields.BooleanField() field = serializers.BooleanField()
class TestNullBooleanField(FieldValues): class TestNullBooleanField(FieldValues):
@ -310,7 +310,7 @@ class TestNullBooleanField(FieldValues):
None: None, None: None,
'other': True 'other': True
} }
field = fields.NullBooleanField() field = serializers.NullBooleanField()
# String types... # String types...
@ -330,7 +330,7 @@ class TestCharField(FieldValues):
1: '1', 1: '1',
'abc': 'abc' 'abc': 'abc'
} }
field = fields.CharField() field = serializers.CharField()
class TestEmailField(FieldValues): class TestEmailField(FieldValues):
@ -345,7 +345,7 @@ class TestEmailField(FieldValues):
'examplecom': ['Enter a valid email address.'] 'examplecom': ['Enter a valid email address.']
} }
outputs = {} outputs = {}
field = fields.EmailField() field = serializers.EmailField()
class TestRegexField(FieldValues): class TestRegexField(FieldValues):
@ -359,7 +359,7 @@ class TestRegexField(FieldValues):
'A9': ["This value does not match the required pattern."] 'A9': ["This value does not match the required pattern."]
} }
outputs = {} outputs = {}
field = fields.RegexField(regex='[a-z][0-9]') field = serializers.RegexField(regex='[a-z][0-9]')
class TestSlugField(FieldValues): class TestSlugField(FieldValues):
@ -373,7 +373,7 @@ class TestSlugField(FieldValues):
'slug 99': ["Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."] 'slug 99': ["Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."]
} }
outputs = {} outputs = {}
field = fields.SlugField() field = serializers.SlugField()
class TestURLField(FieldValues): class TestURLField(FieldValues):
@ -387,7 +387,7 @@ class TestURLField(FieldValues):
'example.com': ['Enter a valid URL.'] 'example.com': ['Enter a valid URL.']
} }
outputs = {} outputs = {}
field = fields.URLField() field = serializers.URLField()
# Number types... # Number types...
@ -415,7 +415,7 @@ class TestIntegerField(FieldValues):
1.0: 1, 1.0: 1,
0.0: 0 0.0: 0
} }
field = fields.IntegerField() field = serializers.IntegerField()
class TestMinMaxIntegerField(FieldValues): class TestMinMaxIntegerField(FieldValues):
@ -435,7 +435,7 @@ class TestMinMaxIntegerField(FieldValues):
'4': ['Ensure this value is less than or equal to 3.'], '4': ['Ensure this value is less than or equal to 3.'],
} }
outputs = {} outputs = {}
field = fields.IntegerField(min_value=1, max_value=3) field = serializers.IntegerField(min_value=1, max_value=3)
class TestFloatField(FieldValues): class TestFloatField(FieldValues):
@ -461,7 +461,7 @@ class TestFloatField(FieldValues):
1.0: 1.0, 1.0: 1.0,
0.0: 0.0, 0.0: 0.0,
} }
field = fields.FloatField() field = serializers.FloatField()
class TestMinMaxFloatField(FieldValues): class TestMinMaxFloatField(FieldValues):
@ -483,7 +483,7 @@ class TestMinMaxFloatField(FieldValues):
'3.1': ['Ensure this value is less than or equal to 3.'], '3.1': ['Ensure this value is less than or equal to 3.'],
} }
outputs = {} outputs = {}
field = fields.FloatField(min_value=1, max_value=3) field = serializers.FloatField(min_value=1, max_value=3)
class TestDecimalField(FieldValues): class TestDecimalField(FieldValues):
@ -518,7 +518,7 @@ class TestDecimalField(FieldValues):
Decimal('1.09'): '1.1', Decimal('1.09'): '1.1',
Decimal('0.04'): '0.0' Decimal('0.04'): '0.0'
} }
field = fields.DecimalField(max_digits=3, decimal_places=1) field = serializers.DecimalField(max_digits=3, decimal_places=1)
class TestMinMaxDecimalField(FieldValues): class TestMinMaxDecimalField(FieldValues):
@ -534,7 +534,7 @@ class TestMinMaxDecimalField(FieldValues):
'20.1': ['Ensure this value is less than or equal to 20.'], '20.1': ['Ensure this value is less than or equal to 20.'],
} }
outputs = {} outputs = {}
field = fields.DecimalField( field = serializers.DecimalField(
max_digits=3, decimal_places=1, max_digits=3, decimal_places=1,
min_value=10, max_value=20 min_value=10, max_value=20
) )
@ -554,13 +554,13 @@ class TestNoStringCoercionDecimalField(FieldValues):
Decimal('1.09'): Decimal('1.1'), Decimal('1.09'): Decimal('1.1'),
Decimal('0.04'): Decimal('0.0'), Decimal('0.04'): Decimal('0.0'),
} }
field = fields.DecimalField( field = serializers.DecimalField(
max_digits=3, decimal_places=1, max_digits=3, decimal_places=1,
coerce_to_string=False coerce_to_string=False
) )
# Date & time fields... # Date & time serializers...
class TestDateField(FieldValues): class TestDateField(FieldValues):
""" """
@ -578,7 +578,7 @@ class TestDateField(FieldValues):
outputs = { outputs = {
datetime.date(2001, 1, 1): '2001-01-01' datetime.date(2001, 1, 1): '2001-01-01'
} }
field = fields.DateField() field = serializers.DateField()
class TestCustomInputFormatDateField(FieldValues): class TestCustomInputFormatDateField(FieldValues):
@ -592,7 +592,7 @@ class TestCustomInputFormatDateField(FieldValues):
'2001-01-01': ['Date has wrong format. Use one of these formats instead: DD [Jan-Dec] YYYY'] '2001-01-01': ['Date has wrong format. Use one of these formats instead: DD [Jan-Dec] YYYY']
} }
outputs = {} outputs = {}
field = fields.DateField(input_formats=['%d %b %Y']) field = serializers.DateField(input_formats=['%d %b %Y'])
class TestCustomOutputFormatDateField(FieldValues): class TestCustomOutputFormatDateField(FieldValues):
@ -604,7 +604,7 @@ class TestCustomOutputFormatDateField(FieldValues):
outputs = { outputs = {
datetime.date(2001, 1, 1): '01 Jan 2001' datetime.date(2001, 1, 1): '01 Jan 2001'
} }
field = fields.DateField(format='%d %b %Y') field = serializers.DateField(format='%d %b %Y')
class TestNoOutputFormatDateField(FieldValues): class TestNoOutputFormatDateField(FieldValues):
@ -616,7 +616,7 @@ class TestNoOutputFormatDateField(FieldValues):
outputs = { outputs = {
datetime.date(2001, 1, 1): datetime.date(2001, 1, 1) datetime.date(2001, 1, 1): datetime.date(2001, 1, 1)
} }
field = fields.DateField(format=None) field = serializers.DateField(format=None)
class TestDateTimeField(FieldValues): class TestDateTimeField(FieldValues):
@ -641,7 +641,7 @@ class TestDateTimeField(FieldValues):
datetime.datetime(2001, 1, 1, 13, 00): '2001-01-01T13:00:00', datetime.datetime(2001, 1, 1, 13, 00): '2001-01-01T13:00:00',
datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()): '2001-01-01T13:00:00Z' datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()): '2001-01-01T13:00:00Z'
} }
field = fields.DateTimeField(default_timezone=timezone.UTC()) field = serializers.DateTimeField(default_timezone=timezone.UTC())
class TestCustomInputFormatDateTimeField(FieldValues): class TestCustomInputFormatDateTimeField(FieldValues):
@ -655,7 +655,7 @@ class TestCustomInputFormatDateTimeField(FieldValues):
'2001-01-01T20:50': ['Datetime has wrong format. Use one of these formats instead: hh:mm[AM|PM], DD [Jan-Dec] YYYY'] '2001-01-01T20:50': ['Datetime has wrong format. Use one of these formats instead: hh:mm[AM|PM], DD [Jan-Dec] YYYY']
} }
outputs = {} outputs = {}
field = fields.DateTimeField(default_timezone=timezone.UTC(), input_formats=['%I:%M%p, %d %b %Y']) field = serializers.DateTimeField(default_timezone=timezone.UTC(), input_formats=['%I:%M%p, %d %b %Y'])
class TestCustomOutputFormatDateTimeField(FieldValues): class TestCustomOutputFormatDateTimeField(FieldValues):
@ -667,7 +667,7 @@ class TestCustomOutputFormatDateTimeField(FieldValues):
outputs = { outputs = {
datetime.datetime(2001, 1, 1, 13, 00): '01:00PM, 01 Jan 2001', datetime.datetime(2001, 1, 1, 13, 00): '01:00PM, 01 Jan 2001',
} }
field = fields.DateTimeField(format='%I:%M%p, %d %b %Y') field = serializers.DateTimeField(format='%I:%M%p, %d %b %Y')
class TestNoOutputFormatDateTimeField(FieldValues): class TestNoOutputFormatDateTimeField(FieldValues):
@ -679,7 +679,7 @@ class TestNoOutputFormatDateTimeField(FieldValues):
outputs = { outputs = {
datetime.datetime(2001, 1, 1, 13, 00): datetime.datetime(2001, 1, 1, 13, 00), datetime.datetime(2001, 1, 1, 13, 00): datetime.datetime(2001, 1, 1, 13, 00),
} }
field = fields.DateTimeField(format=None) field = serializers.DateTimeField(format=None)
class TestNaiveDateTimeField(FieldValues): class TestNaiveDateTimeField(FieldValues):
@ -692,7 +692,7 @@ class TestNaiveDateTimeField(FieldValues):
} }
invalid_inputs = {} invalid_inputs = {}
outputs = {} outputs = {}
field = fields.DateTimeField(default_timezone=None) field = serializers.DateTimeField(default_timezone=None)
class TestTimeField(FieldValues): class TestTimeField(FieldValues):
@ -710,7 +710,7 @@ class TestTimeField(FieldValues):
outputs = { outputs = {
datetime.time(13, 00): '13:00:00' datetime.time(13, 00): '13:00:00'
} }
field = fields.TimeField() field = serializers.TimeField()
class TestCustomInputFormatTimeField(FieldValues): class TestCustomInputFormatTimeField(FieldValues):
@ -724,7 +724,7 @@ class TestCustomInputFormatTimeField(FieldValues):
'13:00': ['Time has wrong format. Use one of these formats instead: hh:mm[AM|PM]'], '13:00': ['Time has wrong format. Use one of these formats instead: hh:mm[AM|PM]'],
} }
outputs = {} outputs = {}
field = fields.TimeField(input_formats=['%I:%M%p']) field = serializers.TimeField(input_formats=['%I:%M%p'])
class TestCustomOutputFormatTimeField(FieldValues): class TestCustomOutputFormatTimeField(FieldValues):
@ -736,7 +736,7 @@ class TestCustomOutputFormatTimeField(FieldValues):
outputs = { outputs = {
datetime.time(13, 00): '01:00PM' datetime.time(13, 00): '01:00PM'
} }
field = fields.TimeField(format='%I:%M%p') field = serializers.TimeField(format='%I:%M%p')
class TestNoOutputFormatTimeField(FieldValues): class TestNoOutputFormatTimeField(FieldValues):
@ -748,7 +748,7 @@ class TestNoOutputFormatTimeField(FieldValues):
outputs = { outputs = {
datetime.time(13, 00): datetime.time(13, 00) datetime.time(13, 00): datetime.time(13, 00)
} }
field = fields.TimeField(format=None) field = serializers.TimeField(format=None)
# Choice types... # Choice types...
@ -768,7 +768,7 @@ class TestChoiceField(FieldValues):
outputs = { outputs = {
'good': 'good' 'good': 'good'
} }
field = fields.ChoiceField( field = serializers.ChoiceField(
choices=[ choices=[
('poor', 'Poor quality'), ('poor', 'Poor quality'),
('medium', 'Medium quality'), ('medium', 'Medium quality'),
@ -794,7 +794,7 @@ class TestChoiceFieldWithType(FieldValues):
'1': 1, '1': 1,
1: 1 1: 1
} }
field = fields.ChoiceField( field = serializers.ChoiceField(
choices=[ choices=[
(1, 'Poor quality'), (1, 'Poor quality'),
(2, 'Medium quality'), (2, 'Medium quality'),
@ -819,7 +819,7 @@ class TestChoiceFieldWithListChoices(FieldValues):
outputs = { outputs = {
'good': 'good' 'good': 'good'
} }
field = fields.ChoiceField(choices=('poor', 'medium', 'good')) field = serializers.ChoiceField(choices=('poor', 'medium', 'good'))
class TestMultipleChoiceField(FieldValues): class TestMultipleChoiceField(FieldValues):
@ -838,7 +838,7 @@ class TestMultipleChoiceField(FieldValues):
outputs = [ outputs = [
(['aircon', 'manual'], set(['aircon', 'manual'])) (['aircon', 'manual'], set(['aircon', 'manual']))
] ]
field = fields.MultipleChoiceField( field = serializers.MultipleChoiceField(
choices=[ choices=[
('aircon', 'AirCon'), ('aircon', 'AirCon'),
('manual', 'Manual drive'), ('manual', 'Manual drive'),
@ -847,7 +847,7 @@ class TestMultipleChoiceField(FieldValues):
) )
# File fields... # File serializers...
class MockFile: class MockFile:
def __init__(self, name='', size=0, url=''): def __init__(self, name='', size=0, url=''):
@ -881,7 +881,7 @@ class TestFileField(FieldValues):
(MockFile(name='example.txt', url='/example.txt'), '/example.txt'), (MockFile(name='example.txt', url='/example.txt'), '/example.txt'),
('', None) ('', None)
] ]
field = fields.FileField(max_length=10) field = serializers.FileField(max_length=10)
class TestFieldFieldWithName(FieldValues): class TestFieldFieldWithName(FieldValues):
@ -893,14 +893,14 @@ class TestFieldFieldWithName(FieldValues):
outputs = [ outputs = [
(MockFile(name='example.txt', url='/example.txt'), 'example.txt') (MockFile(name='example.txt', url='/example.txt'), 'example.txt')
] ]
field = fields.FileField(use_url=False) field = serializers.FileField(use_url=False)
# Stub out mock Django `forms.ImageField` class so we don't *actually* # Stub out mock Django `forms.ImageField` class so we don't *actually*
# call into it's regular validation, or require PIL for testing. # call into it's regular validation, or require PIL for testing.
class FailImageValidation(object): class FailImageValidation(object):
def to_python(self, value): def to_python(self, value):
raise exceptions.ValidationFailed(self.error_messages['invalid_image']) raise serializers.ValidationError(self.error_messages['invalid_image'])
class PassImageValidation(object): class PassImageValidation(object):
@ -917,7 +917,7 @@ class TestInvalidImageField(FieldValues):
(MockFile(name='example.txt', size=10), ['Upload a valid image. The file you uploaded was either not an image or a corrupted image.']) (MockFile(name='example.txt', size=10), ['Upload a valid image. The file you uploaded was either not an image or a corrupted image.'])
] ]
outputs = {} outputs = {}
field = fields.ImageField(_DjangoImageField=FailImageValidation) field = serializers.ImageField(_DjangoImageField=FailImageValidation)
class TestValidImageField(FieldValues): class TestValidImageField(FieldValues):
@ -929,10 +929,10 @@ class TestValidImageField(FieldValues):
] ]
invalid_inputs = {} invalid_inputs = {}
outputs = {} outputs = {}
field = fields.ImageField(_DjangoImageField=PassImageValidation) field = serializers.ImageField(_DjangoImageField=PassImageValidation)
# Composite fields... # Composite serializers...
class TestListField(FieldValues): class TestListField(FieldValues):
""" """
@ -950,7 +950,7 @@ class TestListField(FieldValues):
([1, 2, 3], [1, 2, 3]), ([1, 2, 3], [1, 2, 3]),
(['1', '2', '3'], [1, 2, 3]) (['1', '2', '3'], [1, 2, 3])
] ]
field = fields.ListField(child=fields.IntegerField()) field = serializers.ListField(child=serializers.IntegerField())
# Tests for FieldField. # Tests for FieldField.
@ -963,7 +963,7 @@ class MockRequest:
class TestFileFieldContext: class TestFileFieldContext:
def test_fully_qualified_when_request_in_context(self): def test_fully_qualified_when_request_in_context(self):
field = fields.FileField(max_length=10) field = serializers.FileField(max_length=10)
field._context = {'request': MockRequest()} field._context = {'request': MockRequest()}
obj = MockFile(name='example.txt', url='/example.txt') obj = MockFile(name='example.txt', url='/example.txt')
value = field.to_representation(obj) value = field.to_representation(obj)

View File

@ -1,6 +1,6 @@
from .utils import mock_reverse, fail_reverse, BadType, MockObject, MockQueryset from .utils import mock_reverse, fail_reverse, BadType, MockObject, MockQueryset
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from rest_framework import exceptions, serializers from rest_framework import serializers
from rest_framework.test import APISimpleTestCase from rest_framework.test import APISimpleTestCase
import pytest import pytest
@ -30,13 +30,13 @@ class TestPrimaryKeyRelatedField(APISimpleTestCase):
assert instance is self.instance assert instance is self.instance
def test_pk_related_lookup_does_not_exist(self): def test_pk_related_lookup_does_not_exist(self):
with pytest.raises(exceptions.ValidationFailed) as excinfo: with pytest.raises(serializers.ValidationError) as excinfo:
self.field.to_internal_value(4) self.field.to_internal_value(4)
msg = excinfo.value.detail[0] msg = excinfo.value.detail[0]
assert msg == "Invalid pk '4' - object does not exist." assert msg == "Invalid pk '4' - object does not exist."
def test_pk_related_lookup_invalid_type(self): def test_pk_related_lookup_invalid_type(self):
with pytest.raises(exceptions.ValidationFailed) as excinfo: with pytest.raises(serializers.ValidationError) as excinfo:
self.field.to_internal_value(BadType()) self.field.to_internal_value(BadType())
msg = excinfo.value.detail[0] msg = excinfo.value.detail[0]
assert msg == 'Incorrect type. Expected pk value, received BadType.' assert msg == 'Incorrect type. Expected pk value, received BadType.'
@ -120,13 +120,13 @@ class TestSlugRelatedField(APISimpleTestCase):
assert instance is self.instance assert instance is self.instance
def test_slug_related_lookup_does_not_exist(self): def test_slug_related_lookup_does_not_exist(self):
with pytest.raises(exceptions.ValidationFailed) as excinfo: with pytest.raises(serializers.ValidationError) as excinfo:
self.field.to_internal_value('doesnotexist') self.field.to_internal_value('doesnotexist')
msg = excinfo.value.detail[0] msg = excinfo.value.detail[0]
assert msg == 'Object with name=doesnotexist does not exist.' assert msg == 'Object with name=doesnotexist does not exist.'
def test_slug_related_lookup_invalid_type(self): def test_slug_related_lookup_invalid_type(self):
with pytest.raises(exceptions.ValidationFailed) as excinfo: with pytest.raises(serializers.ValidationError) as excinfo:
self.field.to_internal_value(BadType()) self.field.to_internal_value(BadType())
msg = excinfo.value.detail[0] msg = excinfo.value.detail[0]
assert msg == 'Invalid value.' assert msg == 'Invalid value.'

View File

@ -2,7 +2,7 @@ from __future__ import unicode_literals
from django.core.validators import MaxValueValidator from django.core.validators import MaxValueValidator
from django.db import models from django.db import models
from django.test import TestCase from django.test import TestCase
from rest_framework import exceptions, generics, serializers, status from rest_framework import generics, serializers, status
from rest_framework.test import APIRequestFactory from rest_framework.test import APIRequestFactory
factory = APIRequestFactory() factory = APIRequestFactory()
@ -37,7 +37,7 @@ class ShouldValidateModelSerializer(serializers.ModelSerializer):
def validate_renamed(self, value): def validate_renamed(self, value):
if len(value) < 3: if len(value) < 3:
raise exceptions.ValidationFailed('Minimum 3 characters.') raise serializers.ValidationError('Minimum 3 characters.')
return value return value
class Meta: class Meta:
@ -73,10 +73,10 @@ class ValidationSerializer(serializers.Serializer):
foo = serializers.CharField() foo = serializers.CharField()
def validate_foo(self, attrs, source): def validate_foo(self, attrs, source):
raise exceptions.ValidationFailed("foo invalid") raise serializers.ValidationError("foo invalid")
def validate(self, attrs): def validate(self, attrs):
raise exceptions.ValidationFailed("serializer invalid") raise serializers.ValidationError("serializer invalid")
class TestAvoidValidation(TestCase): class TestAvoidValidation(TestCase):
@ -158,7 +158,7 @@ class TestChoiceFieldChoicesValidate(TestCase):
value = self.CHOICES[0][0] value = self.CHOICES[0][0]
try: try:
f.to_internal_value(value) f.to_internal_value(value)
except exceptions.ValidationFailed: except serializers.ValidationError:
self.fail("Value %s does not validate" % str(value)) self.fail("Value %s does not validate" % str(value))
# def test_nested_choices(self): # def test_nested_choices(self):