Update default error messages for translations

Make use of already translated strings in Django,
wherever possible and makes sense.
This commit is contained in:
José Padilla 2014-12-07 23:52:28 -04:00
parent 6d8a652ef6
commit f076caebb0
8 changed files with 215 additions and 109 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`, # We pass the object instance onto `to_representation`,
# not just the field attribute. # not just the field attribute.
return obj return obj
def to_representation(self, obj): def to_representation(self, obj):
""" """
Serialize the object's class name. 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: The `.fail()` method is a shortcut for raising `ValidationError` that takes a message string from the `error_messages` dictionary. For example:
default_error_messages = { 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(#,#,#)`.', 'incorrect_format': 'Incorrect format. Expected `rgb(#,#,#)`.',
'out_of_range': 'Value out of range. Must be between 0 and 255.' 'out_of_range': 'Value out of range. Must be between 0 and 255.'
} }

View File

@ -6,7 +6,7 @@ from django.forms import ImageField as DjangoImageField
from django.utils import six, timezone from django.utils import six, timezone
from django.utils.dateparse import parse_date, parse_datetime, parse_time from django.utils.dateparse import parse_date, parse_datetime, parse_time
from django.utils.encoding import is_protected_type, smart_text 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 import ISO_8601
from rest_framework.compat import ( from rest_framework.compat import (
EmailValidator, MinValueValidator, MaxValueValidator, EmailValidator, MinValueValidator, MaxValueValidator,
@ -147,7 +147,7 @@ class Field(object):
default_error_messages = { default_error_messages = {
'required': _('This field is required.'), 'required': _('This field is required.'),
'null': _('This field may not be null.') 'null': _('This field cannot be null.')
} }
default_validators = [] default_validators = []
default_empty_html = empty default_empty_html = empty
@ -369,7 +369,7 @@ class Field(object):
class_name = self.__class__.__name__ class_name = self.__class__.__name__
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 % kwargs
raise ValidationError(message_string) raise ValidationError(message_string)
@property @property
@ -429,7 +429,7 @@ class Field(object):
class BooleanField(Field): class BooleanField(Field):
default_error_messages = { default_error_messages = {
'invalid': _('`{input}` is not a valid boolean.') 'invalid': _('`%(input)s` is not a valid boolean.')
} }
default_empty_html = False default_empty_html = False
initial = False initial = False
@ -457,7 +457,7 @@ class BooleanField(Field):
class NullBooleanField(Field): class NullBooleanField(Field):
default_error_messages = { default_error_messages = {
'invalid': _('`{input}` is not a valid boolean.') 'invalid': _('`%(input)s` is not a valid boolean.')
} }
initial = None initial = None
TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True)) TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True))
@ -492,9 +492,15 @@ class NullBooleanField(Field):
class CharField(Field): class CharField(Field):
default_error_messages = { default_error_messages = {
'blank': _('This field may not be blank.'), 'blank': _('This field cannot be blank.'),
'max_length': _('Ensure this field has no more than {max_length} characters.'), 'max_length': ungettext_lazy(
'min_length': _('Ensure this field has no more than {min_length} characters.') '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 = '' initial = ''
coerce_blank_to_null = False coerce_blank_to_null = False
@ -502,15 +508,9 @@ class CharField(Field):
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.allow_blank = kwargs.pop('allow_blank', False) self.allow_blank = kwargs.pop('allow_blank', False)
max_length = kwargs.pop('max_length', None) self.max_length = kwargs.pop('max_length', None)
min_length = kwargs.pop('min_length', None) self.min_length = kwargs.pop('min_length', None)
super(CharField, self).__init__(**kwargs) 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): def run_validation(self, data=empty):
# Test for the empty string here so that it does not get validated, # Test for the empty string here so that it does not get validated,
@ -520,6 +520,17 @@ class CharField(Field):
if not self.allow_blank: if not self.allow_blank:
self.fail('blank') self.fail('blank')
return '' 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) return super(CharField, self).run_validation(data)
def to_internal_value(self, data): def to_internal_value(self, data):
@ -571,7 +582,7 @@ class SlugField(CharField):
class URLField(CharField): class URLField(CharField):
default_error_messages = { default_error_messages = {
'invalid': _("Enter a valid URL.") 'invalid': _('Enter a valid URL.')
} }
def __init__(self, **kwargs): def __init__(self, **kwargs):
@ -585,8 +596,8 @@ class URLField(CharField):
class IntegerField(Field): class IntegerField(Field):
default_error_messages = { default_error_messages = {
'invalid': _('A valid integer is required.'), 'invalid': _('A valid integer is required.'),
'max_value': _('Ensure this value is less than or equal to {max_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 {min_value}.'), 'min_value': _('Ensure this value is greater than or equal to %(limit_value)s.'),
'max_string_length': _('String value too large') 'max_string_length': _('String value too large')
} }
MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs.
@ -596,10 +607,10 @@ class IntegerField(Field):
min_value = kwargs.pop('min_value', None) min_value = kwargs.pop('min_value', None)
super(IntegerField, self).__init__(**kwargs) super(IntegerField, self).__init__(**kwargs)
if max_value is not None: 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)) self.validators.append(MaxValueValidator(max_value, message=message))
if min_value is not None: 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)) self.validators.append(MinValueValidator(min_value, message=message))
def to_internal_value(self, data): def to_internal_value(self, data):
@ -618,9 +629,9 @@ class IntegerField(Field):
class FloatField(Field): class FloatField(Field):
default_error_messages = { default_error_messages = {
'invalid': _("A valid number is required."), 'invalid': _('A valid number is required.'),
'max_value': _('Ensure this value is less than or equal to {max_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 {min_value}.'), 'min_value': _('Ensure this value is greater than or equal to %(limit_value)s.'),
'max_string_length': _('String value too large') 'max_string_length': _('String value too large')
} }
MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs.
@ -630,10 +641,12 @@ class FloatField(Field):
min_value = kwargs.pop('min_value', None) min_value = kwargs.pop('min_value', None)
super(FloatField, self).__init__(**kwargs) super(FloatField, self).__init__(**kwargs)
if max_value is not None: 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)) self.validators.append(MaxValueValidator(max_value, message=message))
if min_value is not None: 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)) self.validators.append(MinValueValidator(min_value, message=message))
def to_internal_value(self, data): def to_internal_value(self, data):
@ -652,11 +665,20 @@ class FloatField(Field):
class DecimalField(Field): class DecimalField(Field):
default_error_messages = { default_error_messages = {
'invalid': _('A valid number is required.'), 'invalid': _('A valid number is required.'),
'max_value': _('Ensure this value is less than or equal to {max_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 {min_value}.'), 'min_value': _('Ensure this value is greater than or equal to %(limit_value)s.'),
'max_digits': _('Ensure that there are no more than {max_digits} digits in total.'), 'max_digits': ungettext_lazy(
'max_decimal_places': _('Ensure that there are no more than {max_decimal_places} decimal places.'), 'Ensure that there are no more than %(max)s digit in total.',
'max_whole_digits': _('Ensure that there are no more than {max_whole_digits} digits before the decimal point.'), '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': _('String value too large')
} }
MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs. MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs.
@ -669,10 +691,12 @@ class DecimalField(Field):
self.coerce_to_string = coerce_to_string if (coerce_to_string is not None) else self.coerce_to_string self.coerce_to_string = coerce_to_string if (coerce_to_string is not None) else self.coerce_to_string
super(DecimalField, self).__init__(**kwargs) super(DecimalField, self).__init__(**kwargs)
if max_value is not None: 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)) self.validators.append(MaxValueValidator(max_value, message=message))
if min_value is not None: 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)) self.validators.append(MinValueValidator(min_value, message=message))
def to_internal_value(self, data): def to_internal_value(self, data):
@ -713,11 +737,11 @@ class DecimalField(Field):
whole_digits = digits - decimals whole_digits = digits - decimals
if self.max_digits is not None and digits > self.max_digits: 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: 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): 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 return value
@ -740,7 +764,7 @@ class DecimalField(Field):
class DateTimeField(Field): class DateTimeField(Field):
default_error_messages = { 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.'), 'date': _('Expected a datetime but got a date.'),
} }
format = api_settings.DATETIME_FORMAT format = api_settings.DATETIME_FORMAT
@ -805,7 +829,7 @@ class DateTimeField(Field):
class DateField(Field): class DateField(Field):
default_error_messages = { 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.'), 'datetime': _('Expected a date but got a datetime.'),
} }
format = api_settings.DATE_FORMAT format = api_settings.DATE_FORMAT
@ -863,7 +887,7 @@ class DateField(Field):
class TimeField(Field): class TimeField(Field):
default_error_messages = { 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 format = api_settings.TIME_FORMAT
input_formats = api_settings.TIME_INPUT_FORMATS input_formats = api_settings.TIME_INPUT_FORMATS
@ -919,7 +943,7 @@ class TimeField(Field):
class ChoiceField(Field): class ChoiceField(Field):
default_error_messages = { 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): def __init__(self, choices, **kwargs):
@ -948,7 +972,7 @@ class ChoiceField(Field):
try: try:
return self.choice_strings_to_values[six.text_type(data)] return self.choice_strings_to_values[six.text_type(data)]
except KeyError: except KeyError:
self.fail('invalid_choice', input=data) self.fail('invalid_choice', value=data)
def to_representation(self, value): def to_representation(self, value):
if value in ('', None): if value in ('', None):
@ -958,8 +982,8 @@ class ChoiceField(Field):
class MultipleChoiceField(ChoiceField): class MultipleChoiceField(ChoiceField):
default_error_messages = { default_error_messages = {
'invalid_choice': _('`{input}` is not a valid choice.'), 'invalid_choice': _('Value %(value)r is not a valid choice.'),
'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`.')
} }
default_empty_html = [] default_empty_html = []
@ -989,11 +1013,14 @@ class MultipleChoiceField(ChoiceField):
class FileField(Field): class FileField(Field):
default_error_messages = { default_error_messages = {
'required': _("No file was submitted."), 'required': _('No file was submitted.'),
'invalid': _("The submitted data was not a file. Check the encoding type on the form."), 'invalid': _('The submitted data was not a file. Check the encoding type on the form.'),
'no_name': _("No filename could be determined."), 'no_name': _('No filename could be determined.'),
'empty': _("The submitted file is empty."), 'empty': _('The submitted file is empty.'),
'max_length': _('Ensure this filename has at most {max_length} characters (it has {length}).'), '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 use_url = api_settings.UPLOADED_FILES_USE_URL
@ -1016,7 +1043,7 @@ class FileField(Field):
if not self.allow_empty_file and not file_size: if not self.allow_empty_file and not file_size:
self.fail('empty') self.fail('empty')
if self.max_length and len(file_name) > self.max_length: 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 return data
@ -1061,7 +1088,7 @@ class ListField(Field):
child = None child = None
initial = [] initial = []
default_error_messages = { 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): def __init__(self, *args, **kwargs):
@ -1192,18 +1219,26 @@ class ModelField(Field):
that do not have a serializer field to be mapped to. that do not have a serializer field to be mapped to.
""" """
default_error_messages = { 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): def __init__(self, model_field, **kwargs):
self.model_field = model_field self.model_field = model_field
# The `max_length` option is supported by Django's base `Field` class, # The `max_length` option is supported by Django's base `Field` class,
# so we'd better support it here. # 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) super(ModelField, self).__init__(**kwargs)
if max_length is not None:
message = self.error_messages['max_length'].format(max_length=max_length) def run_validation(self, data=empty):
self.validators.append(MaxLengthValidator(max_length, message=message)) 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): def to_internal_value(self, data):
rel = getattr(self.model_field, 'rel', None) rel = getattr(self.model_field, 'rel', None)

View File

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

View File

@ -39,7 +39,7 @@ class TestSimpleBoundField:
serializer.is_valid() serializer.is_valid()
assert serializer['text'].value == 'x' * 1000 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['text'].name == 'text'
assert serializer['amount'].value is 123 assert serializer['amount'].value is 123
assert serializer['amount'].errors is None assert serializer['amount'].errors is None

View File

@ -49,7 +49,7 @@ class TestEmpty:
field = serializers.IntegerField() field = serializers.IntegerField()
with pytest.raises(serializers.ValidationError) 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 cannot be null.']
def test_allow_null(self): def test_allow_null(self):
""" """
@ -66,7 +66,7 @@ class TestEmpty:
field = serializers.CharField() field = serializers.CharField()
with pytest.raises(serializers.ValidationError) 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 cannot be blank.']
def test_allow_blank(self): def test_allow_blank(self):
""" """
@ -290,6 +290,24 @@ class FieldValues:
for output_value, expected_output in get_items(self.outputs): for output_value, expected_output in get_items(self.outputs):
assert self.field.to_representation(output_value) == expected_output 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... # Boolean types...
@ -309,7 +327,11 @@ class TestBooleanField(FieldValues):
} }
invalid_inputs = { invalid_inputs = {
'foo': ['`foo` is not a valid boolean.'], '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 = { outputs = {
'true': True, 'true': True,
@ -363,7 +385,10 @@ class TestCharField(FieldValues):
'abc': 'abc' 'abc': 'abc'
} }
invalid_inputs = { 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 = { outputs = {
1: '1', 1: '1',
@ -383,6 +408,9 @@ class TestEmailField(FieldValues):
invalid_inputs = { invalid_inputs = {
'examplecom': ['Enter a valid email address.'] 'examplecom': ['Enter a valid email address.']
} }
es_invalid_inputs = {
'examplecom': [u'Introduzca una dirección de correo electrónico válida.']
}
outputs = {} outputs = {}
field = serializers.EmailField() field = serializers.EmailField()
@ -398,7 +426,8 @@ class TestRegexField(FieldValues):
'A9': ["This value does not match the required pattern."] 'A9': ["This value does not match the required pattern."]
} }
outputs = {} outputs = {}
field = serializers.RegexField(regex='[a-z][0-9]') field_kwargs = {'regex': '[a-z][0-9]'}
field = serializers.RegexField(**field_kwargs)
class TestSlugField(FieldValues): class TestSlugField(FieldValues):
@ -411,6 +440,9 @@ class TestSlugField(FieldValues):
invalid_inputs = { invalid_inputs = {
'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."]
} }
es_invalid_inputs = {
'slug 99': [u"Introduzca un 'slug' válido, consistente en letras, números, guiones bajos o medios."]
}
outputs = {} outputs = {}
field = serializers.SlugField() field = serializers.SlugField()
@ -425,6 +457,9 @@ class TestURLField(FieldValues):
invalid_inputs = { invalid_inputs = {
'example.com': ['Enter a valid URL.'] 'example.com': ['Enter a valid URL.']
} }
es_invalid_inputs = {
'example.com': [u'Introduzca una URL válida.']
}
outputs = {} outputs = {}
field = serializers.URLField() field = serializers.URLField()
@ -446,6 +481,9 @@ class TestIntegerField(FieldValues):
invalid_inputs = { invalid_inputs = {
'abc': ['A valid integer is required.'] 'abc': ['A valid integer is required.']
} }
es_invalid_inputs = {
'abc': ['A valid integer is required.']
}
outputs = { outputs = {
'1': 1, '1': 1,
'0': 0, '0': 0,
@ -480,18 +518,11 @@ class TestMinMaxIntegerField(FieldValues):
'4': [u'Asegúrese de que este valor es menor o igual a 3.'], '4': [u'Asegúrese de que este valor es menor o igual a 3.'],
} }
outputs = {} outputs = {}
field = serializers.IntegerField(min_value=1, max_value=3) field_kwargs = {
'min_value': 1,
@override_settings(LANGUAGE_CODE='es') 'max_value': 3,
def test_invalid_inputs_translated(self): }
""" field = serializers.IntegerField(**field_kwargs)
Ensure that invalid values raise the expected validation error.
"""
field = serializers.IntegerField(min_value=1, max_value=3)
for input_value, expected_failure in get_items(self.es_invalid_inputs):
with pytest.raises(serializers.ValidationError) as exc_info:
field.run_validation(input_value)
assert exc_info.value.detail == expected_failure
class TestFloatField(FieldValues): class TestFloatField(FieldValues):
@ -509,6 +540,9 @@ class TestFloatField(FieldValues):
invalid_inputs = { invalid_inputs = {
'abc': ["A valid number is required."] 'abc': ["A valid number is required."]
} }
es_invalid_inputs = {
'abc': ['A valid number is required.']
}
outputs = { outputs = {
'1': 1.0, '1': 1.0,
'0': 0.0, '0': 0.0,
@ -538,8 +572,15 @@ class TestMinMaxFloatField(FieldValues):
'0.0': ['Ensure this value is greater than or equal to 1.'], '0.0': ['Ensure this value is greater than or equal to 1.'],
'3.1': ['Ensure this value is less than or equal to 3.'], '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 = {} 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): class TestDecimalField(FieldValues):
@ -559,9 +600,17 @@ class TestDecimalField(FieldValues):
(Decimal('Nan'), ["A valid number is required."]), (Decimal('Nan'), ["A valid number is required."]),
(Decimal('Inf'), ["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."]), ('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."]) (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 = { outputs = {
'1': '1.0', '1': '1.0',
'0': '0.0', '0': '0.0',
@ -574,7 +623,8 @@ class TestDecimalField(FieldValues):
Decimal('1.09'): '1.1', Decimal('1.09'): '1.1',
Decimal('0.04'): '0.0' 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): class TestMinMaxDecimalField(FieldValues):
@ -589,11 +639,16 @@ class TestMinMaxDecimalField(FieldValues):
'9.9': ['Ensure this value is greater than or equal to 10.'], '9.9': ['Ensure this value is greater than or equal to 10.'],
'20.1': ['Ensure this value is less than or equal to 20.'], '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 = {} outputs = {}
field = serializers.DecimalField( field_kwargs = {
max_digits=3, decimal_places=1, 'max_digits': 3, 'decimal_places': 1,
min_value=10, max_value=20 'min_value': 10, 'max_value': 20
) }
field = serializers.DecimalField(**field_kwargs)
class TestNoStringCoercionDecimalField(FieldValues): class TestNoStringCoercionDecimalField(FieldValues):
@ -610,10 +665,11 @@ 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 = serializers.DecimalField( field_kwargs = {
max_digits=3, decimal_places=1, 'max_digits': 3, 'decimal_places': 1,
coerce_to_string=False 'coerce_to_string': False
) }
field = serializers.DecimalField(**field_kwargs)
# Date & time serializers... # Date & time serializers...
@ -648,7 +704,8 @@ 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 = serializers.DateField(input_formats=['%d %b %Y']) field_kwargs = {'input_formats': ['%d %b %Y']}
field = serializers.DateField(**field_kwargs)
class TestCustomOutputFormatDateField(FieldValues): class TestCustomOutputFormatDateField(FieldValues):
@ -819,19 +876,23 @@ class TestChoiceField(FieldValues):
'good': 'good', 'good': 'good',
} }
invalid_inputs = { 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 = { outputs = {
'good': 'good', 'good': 'good',
'': '' '': ''
} }
field = serializers.ChoiceField( field_kwargs = {
choices=[ 'choices': [
('poor', 'Poor quality'), ('poor', 'Poor quality'),
('medium', 'Medium quality'), ('medium', 'Medium quality'),
('good', 'Good quality'), ('good', 'Good quality'),
] ]
) }
field = serializers.ChoiceField(**field_kwargs)
class TestChoiceFieldWithType(FieldValues): class TestChoiceFieldWithType(FieldValues):
@ -844,20 +905,25 @@ class TestChoiceFieldWithType(FieldValues):
3: 3, 3: 3,
} }
invalid_inputs = { invalid_inputs = {
5: ['`5` is not a valid choice.'], 5: ['Value 5 is not a valid choice.'],
'abc': ['`abc` 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 = { outputs = {
'1': 1, '1': 1,
1: 1 1: 1
} }
field = serializers.ChoiceField( field_kwargs = {
choices=[ 'choices': [
(1, 'Poor quality'), (1, 'Poor quality'),
(2, 'Medium quality'), (2, 'Medium quality'),
(3, 'Good quality'), (3, 'Good quality'),
] ]
) }
field = serializers.ChoiceField(**field_kwargs)
class TestChoiceFieldWithListChoices(FieldValues): class TestChoiceFieldWithListChoices(FieldValues):
@ -871,12 +937,16 @@ class TestChoiceFieldWithListChoices(FieldValues):
'good': 'good', 'good': 'good',
} }
invalid_inputs = { 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 = { outputs = {
'good': 'good' 'good': 'good'
} }
field = serializers.ChoiceField(choices=('poor', 'medium', 'good')) field_kwargs = {'choices': ('poor', 'medium', 'good')}
field = serializers.ChoiceField(**field_kwargs)
class TestMultipleChoiceField(FieldValues): class TestMultipleChoiceField(FieldValues):
@ -890,18 +960,19 @@ class TestMultipleChoiceField(FieldValues):
} }
invalid_inputs = { invalid_inputs = {
'abc': ['Expected a list of items but got type `str`.'], '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 = [ outputs = [
(['aircon', 'manual'], set(['aircon', 'manual'])) (['aircon', 'manual'], set(['aircon', 'manual']))
] ]
field = serializers.MultipleChoiceField( field_kwargs = {
choices=[ 'choices': [
('aircon', 'AirCon'), ('aircon', 'AirCon'),
('manual', 'Manual drive'), ('manual', 'Manual drive'),
('diesel', 'Diesel'), ('diesel', 'Diesel'),
] ]
) }
field = serializers.MultipleChoiceField(**field_kwargs)
# File serializers... # File serializers...

View File

@ -302,7 +302,7 @@ class HyperlinkedForeignKeyTests(TestCase):
instance = ForeignKeySource.objects.get(pk=1) instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data, context={'request': request}) serializer = ForeignKeySourceSerializer(instance, data=data, context={'request': request})
self.assertFalse(serializer.is_valid()) 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): class HyperlinkedNullableForeignKeyTests(TestCase):

View File

@ -282,7 +282,7 @@ class PKForeignKeyTests(TestCase):
instance = ForeignKeySource.objects.get(pk=1) instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data) serializer = ForeignKeySourceSerializer(instance, data=data)
self.assertFalse(serializer.is_valid()) 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): def test_foreign_key_with_empty(self):
""" """

View File

@ -160,7 +160,7 @@ class SlugForeignKeyTests(TestCase):
instance = ForeignKeySource.objects.get(pk=1) instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data) serializer = ForeignKeySourceSerializer(instance, data=data)
self.assertFalse(serializer.is_valid()) 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): class SlugNullableForeignKeyTests(TestCase):