This commit is contained in:
José Padilla 2014-12-08 09:07:59 +00:00
commit 8057d01594
8 changed files with 235 additions and 99 deletions

View File

@ -491,7 +491,7 @@ As an example, let's create a field that can be used represent the class name of
# We pass the object instance onto `to_representation`,
# not just the field attribute.
return obj
def to_representation(self, obj):
"""
Serialize the object's class name.
@ -522,7 +522,7 @@ To indicate invalid data, we should raise a `serializers.ValidationError`, like
The `.fail()` method is a shortcut for raising `ValidationError` that takes a message string from the `error_messages` dictionary. For example:
default_error_messages = {
'incorrect_type': 'Incorrect type. Expected a string, but got {input_type}',
'incorrect_type': 'Incorrect type. Expected a string, but got %(input_type)s',
'incorrect_format': 'Incorrect format. Expected `rgb(#,#,#)`.',
'out_of_range': 'Value out of range. Must be between 0 and 255.'
}

View File

@ -1,3 +1,4 @@
from __future__ import unicode_literals
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.core.exceptions import ValidationError as DjangoValidationError
@ -6,7 +7,7 @@ from django.forms import ImageField as DjangoImageField
from django.utils import six, timezone
from django.utils.dateparse import parse_date, parse_datetime, parse_time
from django.utils.encoding import is_protected_type, smart_text
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy, ugettext_lazy as _
from rest_framework import ISO_8601
from rest_framework.compat import (
EmailValidator, MinValueValidator, MaxValueValidator,
@ -147,7 +148,7 @@ class Field(object):
default_error_messages = {
'required': _('This field is required.'),
'null': _('This field may not be null.')
'null': _('This field cannot be null.')
}
default_validators = []
default_empty_html = empty
@ -369,7 +370,7 @@ class Field(object):
class_name = self.__class__.__name__
msg = MISSING_ERROR_MESSAGE.format(class_name=class_name, key=key)
raise AssertionError(msg)
message_string = msg.format(**kwargs)
message_string = msg % kwargs
raise ValidationError(message_string)
@property
@ -429,7 +430,7 @@ class Field(object):
class BooleanField(Field):
default_error_messages = {
'invalid': _('`{input}` is not a valid boolean.')
'invalid': _('`%(input)s` is not a valid boolean.')
}
default_empty_html = False
initial = False
@ -457,7 +458,7 @@ class BooleanField(Field):
class NullBooleanField(Field):
default_error_messages = {
'invalid': _('`{input}` is not a valid boolean.')
'invalid': _('`%(input)s` is not a valid boolean.')
}
initial = None
TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True))
@ -492,9 +493,15 @@ class NullBooleanField(Field):
class CharField(Field):
default_error_messages = {
'blank': _('This field may not be blank.'),
'max_length': _('Ensure this field has no more than {max_length} characters.'),
'min_length': _('Ensure this field has no more than {min_length} characters.')
'blank': _('This field cannot be blank.'),
'max_length': ungettext_lazy(
'Ensure this value has at most %(limit_value)d character (it has %(show_value)d).',
'Ensure this value has at most %(limit_value)d characters (it has %(show_value)d).',
'limit_value'),
'min_length': ungettext_lazy(
'Ensure this value has at least %(limit_value)d character (it has %(show_value)d).',
'Ensure this value has at least %(limit_value)d characters (it has %(show_value)d).',
'limit_value')
}
initial = ''
coerce_blank_to_null = False
@ -502,15 +509,9 @@ class CharField(Field):
def __init__(self, **kwargs):
self.allow_blank = kwargs.pop('allow_blank', False)
max_length = kwargs.pop('max_length', None)
min_length = kwargs.pop('min_length', None)
self.max_length = kwargs.pop('max_length', None)
self.min_length = kwargs.pop('min_length', None)
super(CharField, self).__init__(**kwargs)
if max_length is not None:
message = self.error_messages['max_length'].format(max_length=max_length)
self.validators.append(MaxLengthValidator(max_length, message=message))
if min_length is not None:
message = self.error_messages['min_length'].format(min_length=min_length)
self.validators.append(MinLengthValidator(min_length, message=message))
def run_validation(self, data=empty):
# Test for the empty string here so that it does not get validated,
@ -520,6 +521,17 @@ class CharField(Field):
if not self.allow_blank:
self.fail('blank')
return ''
if self.max_length is not None:
params = {'limit_value': self.max_length, 'show_value': data}
message = self.error_messages['max_length'] % params
self.validators.append(MaxLengthValidator(self.max_length, message=message))
if self.min_length is not None:
params = {'limit_value': self.min_length, 'show_value': data}
message = self.error_messages['min_length'] % params
self.validators.append(MinLengthValidator(self.min_length, message=message))
return super(CharField, self).run_validation(data)
def to_internal_value(self, data):
@ -571,7 +583,7 @@ class SlugField(CharField):
class URLField(CharField):
default_error_messages = {
'invalid': _("Enter a valid URL.")
'invalid': _('Enter a valid URL.')
}
def __init__(self, **kwargs):
@ -585,8 +597,8 @@ class URLField(CharField):
class IntegerField(Field):
default_error_messages = {
'invalid': _('A valid integer is required.'),
'max_value': _('Ensure this value is less than or equal to {max_value}.'),
'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
'max_value': _('Ensure this value is less than or equal to %(limit_value)s.'),
'min_value': _('Ensure this value is greater than or equal to %(limit_value)s.'),
'max_string_length': _('String value too large')
}
MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs.
@ -596,10 +608,10 @@ class IntegerField(Field):
min_value = kwargs.pop('min_value', None)
super(IntegerField, self).__init__(**kwargs)
if max_value is not None:
message = self.error_messages['max_value'].format(max_value=max_value)
message = self.error_messages['max_value'] % {'limit_value': max_value}
self.validators.append(MaxValueValidator(max_value, message=message))
if min_value is not None:
message = self.error_messages['min_value'].format(min_value=min_value)
message = self.error_messages['min_value'] % {'limit_value': min_value}
self.validators.append(MinValueValidator(min_value, message=message))
def to_internal_value(self, data):
@ -618,9 +630,9 @@ class IntegerField(Field):
class FloatField(Field):
default_error_messages = {
'invalid': _("A valid number is required."),
'max_value': _('Ensure this value is less than or equal to {max_value}.'),
'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
'invalid': _('A valid number is required.'),
'max_value': _('Ensure this value is less than or equal to %(limit_value)s.'),
'min_value': _('Ensure this value is greater than or equal to %(limit_value)s.'),
'max_string_length': _('String value too large')
}
MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs.
@ -630,10 +642,12 @@ class FloatField(Field):
min_value = kwargs.pop('min_value', None)
super(FloatField, self).__init__(**kwargs)
if max_value is not None:
message = self.error_messages['max_value'].format(max_value=max_value)
params = {'limit_value': max_value}
message = self.error_messages['max_value'] % params
self.validators.append(MaxValueValidator(max_value, message=message))
if min_value is not None:
message = self.error_messages['min_value'].format(min_value=min_value)
params = {'limit_value': min_value}
message = self.error_messages['min_value'] % params
self.validators.append(MinValueValidator(min_value, message=message))
def to_internal_value(self, data):
@ -652,11 +666,20 @@ class FloatField(Field):
class DecimalField(Field):
default_error_messages = {
'invalid': _('A valid number is required.'),
'max_value': _('Ensure this value is less than or equal to {max_value}.'),
'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
'max_digits': _('Ensure that there are no more than {max_digits} digits in total.'),
'max_decimal_places': _('Ensure that there are no more than {max_decimal_places} decimal places.'),
'max_whole_digits': _('Ensure that there are no more than {max_whole_digits} digits before the decimal point.'),
'max_value': _('Ensure this value is less than or equal to %(limit_value)s.'),
'min_value': _('Ensure this value is greater than or equal to %(limit_value)s.'),
'max_digits': ungettext_lazy(
'Ensure that there are no more than %(max)s digit in total.',
'Ensure that there are no more than %(max)s digits in total.',
'max'),
'max_decimal_places': ungettext_lazy(
'Ensure that there are no more than %(max)s decimal place.',
'Ensure that there are no more than %(max)s decimal places.',
'max'),
'max_whole_digits': ungettext_lazy(
'Ensure that there are no more than %(max)s digit before the decimal point.',
'Ensure that there are no more than %(max)s digits before the decimal point.',
'max'),
'max_string_length': _('String value too large')
}
MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs.
@ -669,10 +692,12 @@ class DecimalField(Field):
self.coerce_to_string = coerce_to_string if (coerce_to_string is not None) else self.coerce_to_string
super(DecimalField, self).__init__(**kwargs)
if max_value is not None:
message = self.error_messages['max_value'].format(max_value=max_value)
params = {'limit_value': max_value}
message = self.error_messages['max_value'] % params
self.validators.append(MaxValueValidator(max_value, message=message))
if min_value is not None:
message = self.error_messages['min_value'].format(min_value=min_value)
params = {'limit_value': min_value}
message = self.error_messages['min_value'] % params
self.validators.append(MinValueValidator(min_value, message=message))
def to_internal_value(self, data):
@ -713,11 +738,11 @@ class DecimalField(Field):
whole_digits = digits - decimals
if self.max_digits is not None and digits > self.max_digits:
self.fail('max_digits', max_digits=self.max_digits)
self.fail('max_digits', max=self.max_digits)
if self.decimal_places is not None and decimals > self.decimal_places:
self.fail('max_decimal_places', max_decimal_places=self.decimal_places)
self.fail('max_decimal_places', max=self.decimal_places)
if self.max_digits is not None and self.decimal_places is not None and whole_digits > (self.max_digits - self.decimal_places):
self.fail('max_whole_digits', max_whole_digits=self.max_digits - self.decimal_places)
self.fail('max_whole_digits', max=self.max_digits - self.decimal_places)
return value
@ -740,7 +765,7 @@ class DecimalField(Field):
class DateTimeField(Field):
default_error_messages = {
'invalid': _('Datetime has wrong format. Use one of these formats instead: {format}'),
'invalid': _('Datetime has wrong format. Use one of these formats instead: %(format)s'),
'date': _('Expected a datetime but got a date.'),
}
format = api_settings.DATETIME_FORMAT
@ -805,7 +830,7 @@ class DateTimeField(Field):
class DateField(Field):
default_error_messages = {
'invalid': _('Date has wrong format. Use one of these formats instead: {format}'),
'invalid': _('Date has wrong format. Use one of these formats instead: %(format)s'),
'datetime': _('Expected a date but got a datetime.'),
}
format = api_settings.DATE_FORMAT
@ -863,7 +888,7 @@ class DateField(Field):
class TimeField(Field):
default_error_messages = {
'invalid': _('Time has wrong format. Use one of these formats instead: {format}'),
'invalid': _('Time has wrong format. Use one of these formats instead: %(format)s'),
}
format = api_settings.TIME_FORMAT
input_formats = api_settings.TIME_INPUT_FORMATS
@ -919,7 +944,7 @@ class TimeField(Field):
class ChoiceField(Field):
default_error_messages = {
'invalid_choice': _('`{input}` is not a valid choice.')
'invalid_choice': _('Value %(value)r is not a valid choice.')
}
def __init__(self, choices, **kwargs):
@ -948,7 +973,7 @@ class ChoiceField(Field):
try:
return self.choice_strings_to_values[six.text_type(data)]
except KeyError:
self.fail('invalid_choice', input=data)
self.fail('invalid_choice', value=data)
def to_representation(self, value):
if value in ('', None):
@ -958,8 +983,8 @@ class ChoiceField(Field):
class MultipleChoiceField(ChoiceField):
default_error_messages = {
'invalid_choice': _('`{input}` is not a valid choice.'),
'not_a_list': _('Expected a list of items but got type `{input_type}`.')
'invalid_choice': _('Value %(value)r is not a valid choice.'),
'not_a_list': _('Expected a list of items but got type `%(input_type)s`.')
}
default_empty_html = []
@ -989,11 +1014,14 @@ class MultipleChoiceField(ChoiceField):
class FileField(Field):
default_error_messages = {
'required': _("No file was submitted."),
'invalid': _("The submitted data was not a file. Check the encoding type on the form."),
'no_name': _("No filename could be determined."),
'empty': _("The submitted file is empty."),
'max_length': _('Ensure this filename has at most {max_length} characters (it has {length}).'),
'required': _('No file was submitted.'),
'invalid': _('The submitted data was not a file. Check the encoding type on the form.'),
'no_name': _('No filename could be determined.'),
'empty': _('The submitted file is empty.'),
'max_length': ungettext_lazy(
'Ensure this filename has at most %(max)d character (it has %(length)d).',
'Ensure this filename has at most %(max)d characters (it has %(length)d).',
'max'),
}
use_url = api_settings.UPLOADED_FILES_USE_URL
@ -1016,7 +1044,7 @@ class FileField(Field):
if not self.allow_empty_file and not file_size:
self.fail('empty')
if self.max_length and len(file_name) > self.max_length:
self.fail('max_length', max_length=self.max_length, length=len(file_name))
self.fail('max_length', max=self.max_length, length=len(file_name))
return data
@ -1061,7 +1089,7 @@ class ListField(Field):
child = None
initial = []
default_error_messages = {
'not_a_list': _('Expected a list of items but got type `{input_type}`')
'not_a_list': _('Expected a list of items but got type `%(input_type)s`')
}
def __init__(self, *args, **kwargs):
@ -1192,18 +1220,26 @@ class ModelField(Field):
that do not have a serializer field to be mapped to.
"""
default_error_messages = {
'max_length': _('Ensure this field has no more than {max_length} characters.'),
'max_length': ungettext_lazy(
'Ensure this value has at most %(limit_value)d character (it has %(show_value)d).',
'Ensure this value has at most %(limit_value)d characters (it has %(show_value)d).',
'limit_value'),
}
def __init__(self, model_field, **kwargs):
self.model_field = model_field
# The `max_length` option is supported by Django's base `Field` class,
# so we'd better support it here.
max_length = kwargs.pop('max_length', None)
self.max_length = kwargs.pop('max_length', None)
super(ModelField, self).__init__(**kwargs)
if max_length is not None:
message = self.error_messages['max_length'].format(max_length=max_length)
self.validators.append(MaxLengthValidator(max_length, message=message))
def run_validation(self, data=empty):
if self.max_length is not None:
params = {'limit_value': self.max_length, 'show_value': data}
message = self.error_messages['max_length'] % params
self.validators.append(MaxLengthValidator(self.max_length, message=message))
return super(ModelField, self).run_validation(data)
def to_internal_value(self, data):
rel = getattr(self.model_field, 'rel', None)

View File

@ -116,8 +116,8 @@ class StringRelatedField(RelatedField):
class PrimaryKeyRelatedField(RelatedField):
default_error_messages = {
'required': 'This field is required.',
'does_not_exist': "Invalid pk '{pk_value}' - object does not exist.",
'incorrect_type': 'Incorrect type. Expected pk value, received {data_type}.',
'does_not_exist': "Invalid pk '%(pk_value)s' - object does not exist.",
'incorrect_type': 'Incorrect type. Expected pk value, received %(data_type)s.',
}
def to_internal_value(self, data):
@ -166,7 +166,7 @@ class HyperlinkedRelatedField(RelatedField):
'no_match': 'Invalid hyperlink - No URL match',
'incorrect_match': 'Invalid hyperlink - Incorrect URL match.',
'does_not_exist': 'Invalid hyperlink - Object does not exist.',
'incorrect_type': 'Incorrect type. Expected URL string, received {data_type}.',
'incorrect_type': 'Incorrect type. Expected URL string, received %(data_type)s.',
}
def __init__(self, view_name=None, **kwargs):
@ -293,7 +293,7 @@ class SlugRelatedField(RelatedField):
"""
default_error_messages = {
'does_not_exist': _("Object with {slug_name}={value} does not exist."),
'does_not_exist': _("Object with %(slug_name)s=%(value)s does not exist."),
'invalid': _('Invalid value.'),
}

View File

@ -39,7 +39,7 @@ class TestSimpleBoundField:
serializer.is_valid()
assert serializer['text'].value == 'x' * 1000
assert serializer['text'].errors == ['Ensure this field has no more than 100 characters.']
assert serializer['text'].errors == ['Ensure this value has at most 100 characters (it has 1000).']
assert serializer['text'].name == 'text'
assert serializer['amount'].value is 123
assert serializer['amount'].errors is None

View File

@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
from decimal import Decimal
from django.utils import timezone
from django.test.utils import override_settings
from rest_framework import serializers
import datetime
import django
@ -22,6 +24,16 @@ class TestEmpty:
field.run_validation()
assert exc_info.value.detail == ['This field is required.']
@override_settings(LANGUAGE_CODE='es')
def test_required_translated(self):
"""
By default '' is not a valid input.
"""
field = serializers.IntegerField()
with pytest.raises(serializers.ValidationError) as exc_info:
field.run_validation()
assert exc_info.value.detail == ['Este campo es obligatorio.']
def test_not_required(self):
"""
If `required=False` then a field may be omitted from the input.
@ -37,7 +49,7 @@ class TestEmpty:
field = serializers.IntegerField()
with pytest.raises(serializers.ValidationError) as exc_info:
field.run_validation(None)
assert exc_info.value.detail == ['This field may not be null.']
assert exc_info.value.detail == ['This field cannot be null.']
def test_allow_null(self):
"""
@ -54,7 +66,7 @@ class TestEmpty:
field = serializers.CharField()
with pytest.raises(serializers.ValidationError) as exc_info:
field.run_validation('')
assert exc_info.value.detail == ['This field may not be blank.']
assert exc_info.value.detail == ['This field cannot be blank.']
def test_allow_blank(self):
"""
@ -62,7 +74,7 @@ class TestEmpty:
"""
field = serializers.CharField(allow_blank=True)
output = field.run_validation('')
assert output is ''
assert str(output) is str('')
def test_default(self):
"""
@ -278,6 +290,24 @@ class FieldValues:
for output_value, expected_output in get_items(self.outputs):
assert self.field.to_representation(output_value) == expected_output
@override_settings(LANGUAGE_CODE='es')
def test_invalid_inputs_translated(self):
"""
Ensure that invalid values raise the expected validation localized error.
"""
es_invalid_inputs = get_items(getattr(self, 'es_invalid_inputs', {}))
if not es_invalid_inputs:
return
field_kwargs = getattr(self, 'field_kwargs', {})
field = self.field.__class__(**field_kwargs)
for input_value, expected_failure in es_invalid_inputs:
with pytest.raises(serializers.ValidationError) as exc_info:
field.run_validation(input_value)
assert exc_info.value.detail == expected_failure
# Boolean types...
@ -297,7 +327,11 @@ class TestBooleanField(FieldValues):
}
invalid_inputs = {
'foo': ['`foo` is not a valid boolean.'],
None: ['This field may not be null.']
None: ['This field cannot be null.']
}
es_invalid_inputs = {
'foo': ['`foo` is not a valid boolean.'],
None: ['Este campo no puede ser nulo.']
}
outputs = {
'true': True,
@ -351,7 +385,10 @@ class TestCharField(FieldValues):
'abc': 'abc'
}
invalid_inputs = {
'': ['This field may not be blank.']
'': ['This field cannot be blank.']
}
es_invalid_inputs = {
'': [u'Este campo no puede estar vacío.']
}
outputs = {
1: '1',
@ -371,6 +408,9 @@ class TestEmailField(FieldValues):
invalid_inputs = {
'examplecom': ['Enter a valid email address.']
}
es_invalid_inputs = {
'examplecom': [u'Introduzca una dirección de correo electrónico válida.']
}
outputs = {}
field = serializers.EmailField()
@ -386,7 +426,8 @@ class TestRegexField(FieldValues):
'A9': ["This value does not match the required pattern."]
}
outputs = {}
field = serializers.RegexField(regex='[a-z][0-9]')
field_kwargs = {'regex': '[a-z][0-9]'}
field = serializers.RegexField(**field_kwargs)
class TestSlugField(FieldValues):
@ -399,6 +440,9 @@ class TestSlugField(FieldValues):
invalid_inputs = {
'slug 99': ["Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."]
}
es_invalid_inputs = {
'slug 99': [u"Introduzca un 'slug' válido, consistente en letras, números, guiones bajos o medios."]
}
outputs = {}
field = serializers.SlugField()
@ -413,6 +457,9 @@ class TestURLField(FieldValues):
invalid_inputs = {
'example.com': ['Enter a valid URL.']
}
es_invalid_inputs = {
'example.com': [u'Introduzca una URL válida.']
}
outputs = {}
field = serializers.URLField()
@ -434,6 +481,9 @@ class TestIntegerField(FieldValues):
invalid_inputs = {
'abc': ['A valid integer is required.']
}
es_invalid_inputs = {
'abc': ['A valid integer is required.']
}
outputs = {
'1': 1,
'0': 0,
@ -461,8 +511,18 @@ class TestMinMaxIntegerField(FieldValues):
'0': ['Ensure this value is greater than or equal to 1.'],
'4': ['Ensure this value is less than or equal to 3.'],
}
es_invalid_inputs = {
0: [u'Asegúrese de que este valor es mayor o igual a 1.'],
4: [u'Asegúrese de que este valor es menor o igual a 3.'],
'0': [u'Asegúrese de que este valor es mayor o igual a 1.'],
'4': [u'Asegúrese de que este valor es menor o igual a 3.'],
}
outputs = {}
field = serializers.IntegerField(min_value=1, max_value=3)
field_kwargs = {
'min_value': 1,
'max_value': 3,
}
field = serializers.IntegerField(**field_kwargs)
class TestFloatField(FieldValues):
@ -480,6 +540,9 @@ class TestFloatField(FieldValues):
invalid_inputs = {
'abc': ["A valid number is required."]
}
es_invalid_inputs = {
'abc': ['A valid number is required.']
}
outputs = {
'1': 1.0,
'0': 0.0,
@ -509,8 +572,15 @@ class TestMinMaxFloatField(FieldValues):
'0.0': ['Ensure this value is greater than or equal to 1.'],
'3.1': ['Ensure this value is less than or equal to 3.'],
}
es_invalid_inputs = {
0.9: [u'Asegúrese de que este valor es mayor o igual a 1.'],
3.1: [u'Asegúrese de que este valor es menor o igual a 3.'],
'0.0': [u'Asegúrese de que este valor es mayor o igual a 1.'],
'3.1': [u'Asegúrese de que este valor es menor o igual a 3.'],
}
outputs = {}
field = serializers.FloatField(min_value=1, max_value=3)
field_kwargs = {'min_value': 1, 'max_value': 3}
field = serializers.FloatField(**field_kwargs)
class TestDecimalField(FieldValues):
@ -530,9 +600,17 @@ class TestDecimalField(FieldValues):
(Decimal('Nan'), ["A valid number is required."]),
(Decimal('Inf'), ["A valid number is required."]),
('12.345', ["Ensure that there are no more than 3 digits in total."]),
('0.01', ["Ensure that there are no more than 1 decimal places."]),
('0.01', ["Ensure that there are no more than 1 decimal place."]),
(123, ["Ensure that there are no more than 2 digits before the decimal point."])
)
es_invalid_inputs = (
('abc', ["A valid number is required."]),
(Decimal('Nan'), ["A valid number is required."]),
(Decimal('Inf'), ["A valid number is required."]),
('12.345', [u'Asegúrese de que no hay más de 3 dígitos en total.']),
('0.01', [u'Asegúrese de que no haya más de 1 dígito decimal.']),
(123, [u'Asegúrese de que no haya más de 2 dígitos antes del punto decimal.'])
)
outputs = {
'1': '1.0',
'0': '0.0',
@ -545,7 +623,8 @@ class TestDecimalField(FieldValues):
Decimal('1.09'): '1.1',
Decimal('0.04'): '0.0'
}
field = serializers.DecimalField(max_digits=3, decimal_places=1)
field_kwargs = {'max_digits': 3, 'decimal_places': 1}
field = serializers.DecimalField(**field_kwargs)
class TestMinMaxDecimalField(FieldValues):
@ -560,11 +639,16 @@ class TestMinMaxDecimalField(FieldValues):
'9.9': ['Ensure this value is greater than or equal to 10.'],
'20.1': ['Ensure this value is less than or equal to 20.'],
}
es_invalid_inputs = {
'9.9': [u'Asegúrese de que este valor es mayor o igual a 10.'],
'20.1': [u'Asegúrese de que este valor es menor o igual a 20.'],
}
outputs = {}
field = serializers.DecimalField(
max_digits=3, decimal_places=1,
min_value=10, max_value=20
)
field_kwargs = {
'max_digits': 3, 'decimal_places': 1,
'min_value': 10, 'max_value': 20
}
field = serializers.DecimalField(**field_kwargs)
class TestNoStringCoercionDecimalField(FieldValues):
@ -581,10 +665,11 @@ class TestNoStringCoercionDecimalField(FieldValues):
Decimal('1.09'): Decimal('1.1'),
Decimal('0.04'): Decimal('0.0'),
}
field = serializers.DecimalField(
max_digits=3, decimal_places=1,
coerce_to_string=False
)
field_kwargs = {
'max_digits': 3, 'decimal_places': 1,
'coerce_to_string': False
}
field = serializers.DecimalField(**field_kwargs)
# Date & time serializers...
@ -619,7 +704,8 @@ class TestCustomInputFormatDateField(FieldValues):
'2001-01-01': ['Date has wrong format. Use one of these formats instead: DD [Jan-Dec] YYYY']
}
outputs = {}
field = serializers.DateField(input_formats=['%d %b %Y'])
field_kwargs = {'input_formats': ['%d %b %Y']}
field = serializers.DateField(**field_kwargs)
class TestCustomOutputFormatDateField(FieldValues):
@ -790,19 +876,23 @@ class TestChoiceField(FieldValues):
'good': 'good',
}
invalid_inputs = {
'amazing': ['`amazing` is not a valid choice.']
'amazing': ["Value 'amazing' is not a valid choice."]
}
es_invalid_inputs = {
'amazing': [u"Valor 'amazing' no es una opción válida."]
}
outputs = {
'good': 'good',
'': ''
}
field = serializers.ChoiceField(
choices=[
field_kwargs = {
'choices': [
('poor', 'Poor quality'),
('medium', 'Medium quality'),
('good', 'Good quality'),
]
)
}
field = serializers.ChoiceField(**field_kwargs)
class TestChoiceFieldWithType(FieldValues):
@ -815,20 +905,25 @@ class TestChoiceFieldWithType(FieldValues):
3: 3,
}
invalid_inputs = {
5: ['`5` is not a valid choice.'],
'abc': ['`abc` is not a valid choice.']
5: ['Value 5 is not a valid choice.'],
'abc': ["Value 'abc' is not a valid choice."]
}
es_invalid_inputs = {
5: [u'Valor 5 no es una opción válida.'],
'abc': [u"Valor 'abc' no es una opción válida."]
}
outputs = {
'1': 1,
1: 1
}
field = serializers.ChoiceField(
choices=[
field_kwargs = {
'choices': [
(1, 'Poor quality'),
(2, 'Medium quality'),
(3, 'Good quality'),
]
)
}
field = serializers.ChoiceField(**field_kwargs)
class TestChoiceFieldWithListChoices(FieldValues):
@ -842,12 +937,16 @@ class TestChoiceFieldWithListChoices(FieldValues):
'good': 'good',
}
invalid_inputs = {
'awful': ['`awful` is not a valid choice.']
'awful': ["Value 'awful' is not a valid choice."]
}
es_invalid_inputs = {
'awful': [u"Valor 'awful' no es una opción válida."]
}
outputs = {
'good': 'good'
}
field = serializers.ChoiceField(choices=('poor', 'medium', 'good'))
field_kwargs = {'choices': ('poor', 'medium', 'good')}
field = serializers.ChoiceField(**field_kwargs)
class TestMultipleChoiceField(FieldValues):
@ -861,18 +960,19 @@ class TestMultipleChoiceField(FieldValues):
}
invalid_inputs = {
'abc': ['Expected a list of items but got type `str`.'],
('aircon', 'incorrect'): ['`incorrect` is not a valid choice.']
('aircon', 'incorrect'): ["Value 'incorrect' is not a valid choice."]
}
outputs = [
(['aircon', 'manual'], set(['aircon', 'manual']))
]
field = serializers.MultipleChoiceField(
choices=[
field_kwargs = {
'choices': [
('aircon', 'AirCon'),
('manual', 'Manual drive'),
('diesel', 'Diesel'),
]
)
}
field = serializers.MultipleChoiceField(**field_kwargs)
# File serializers...

View File

@ -302,7 +302,7 @@ class HyperlinkedForeignKeyTests(TestCase):
instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data, context={'request': request})
self.assertFalse(serializer.is_valid())
self.assertEqual(serializer.errors, {'target': ['This field may not be null.']})
self.assertEqual(serializer.errors, {'target': ['This field cannot be null.']})
class HyperlinkedNullableForeignKeyTests(TestCase):

View File

@ -282,7 +282,7 @@ class PKForeignKeyTests(TestCase):
instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data)
self.assertFalse(serializer.is_valid())
self.assertEqual(serializer.errors, {'target': ['This field may not be null.']})
self.assertEqual(serializer.errors, {'target': ['This field cannot be null.']})
def test_foreign_key_with_empty(self):
"""

View File

@ -160,7 +160,7 @@ class SlugForeignKeyTests(TestCase):
instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data)
self.assertFalse(serializer.is_valid())
self.assertEqual(serializer.errors, {'target': ['This field may not be null.']})
self.assertEqual(serializer.errors, {'target': ['This field cannot be null.']})
class SlugNullableForeignKeyTests(TestCase):