This commit is contained in:
Carlton Gibson 2017-11-20 08:36:11 +00:00 committed by GitHub
commit 34d052aefc
4 changed files with 131 additions and 12 deletions

View File

@ -987,8 +987,8 @@ 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': _('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_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_whole_digits': _('Ensure that there are no more than {max_whole_digits} digits before the decimal point.'),

View File

@ -3,6 +3,7 @@ Helper functions for mapping model fields to a dictionary of default
keyword arguments that should be used for their equivalent serializer fields. keyword arguments that should be used for their equivalent serializer fields.
""" """
import inspect import inspect
from collections import OrderedDict
from django.core import validators from django.core import validators
from django.db import models from django.db import models
@ -127,12 +128,14 @@ def get_field_kwargs(field_name, model_field):
else: else:
# Ensure that max_value is passed explicitly as a keyword arg, # Ensure that max_value is passed explicitly as a keyword arg,
# rather than as a validator. # rather than as a validator.
max_value = next(( max_value, message = next((
validator.limit_value for validator in validator_kwarg (validator.limit_value, validator.message) for validator in validator_kwarg
if isinstance(validator, validators.MaxValueValidator) if isinstance(validator, validators.MaxValueValidator)
), None) ), (None, ''))
if max_value is not None and isinstance(model_field, NUMERIC_FIELD_TYPES): if max_value is not None and isinstance(model_field, NUMERIC_FIELD_TYPES):
kwargs['max_value'] = max_value kwargs['max_value'] = max_value
if message != '':
kwargs.setdefault('error_messages', OrderedDict()).update(max_value=message)
validator_kwarg = [ validator_kwarg = [
validator for validator in validator_kwarg validator for validator in validator_kwarg
if not isinstance(validator, validators.MaxValueValidator) if not isinstance(validator, validators.MaxValueValidator)
@ -140,12 +143,14 @@ def get_field_kwargs(field_name, model_field):
# Ensure that min_value is passed explicitly as a keyword arg, # Ensure that min_value is passed explicitly as a keyword arg,
# rather than as a validator. # rather than as a validator.
min_value = next(( min_value, message = next((
validator.limit_value for validator in validator_kwarg (validator.limit_value, validator.message) for validator in validator_kwarg
if isinstance(validator, validators.MinValueValidator) if isinstance(validator, validators.MinValueValidator)
), None) ), (None, ''))
if min_value is not None and isinstance(model_field, NUMERIC_FIELD_TYPES): if min_value is not None and isinstance(model_field, NUMERIC_FIELD_TYPES):
kwargs['min_value'] = min_value kwargs['min_value'] = min_value
if message != '':
kwargs.setdefault('error_messages', OrderedDict()).update(min_value=message)
validator_kwarg = [ validator_kwarg = [
validator for validator in validator_kwarg validator for validator in validator_kwarg
if not isinstance(validator, validators.MinValueValidator) if not isinstance(validator, validators.MinValueValidator)
@ -154,6 +159,9 @@ def get_field_kwargs(field_name, model_field):
# URLField does not need to include the URLValidator argument, # URLField does not need to include the URLValidator argument,
# as it is explicitly added in. # as it is explicitly added in.
if isinstance(model_field, models.URLField): if isinstance(model_field, models.URLField):
custom_message = model_field.error_messages.get("invalid", None)
if custom_message is not None:
kwargs.setdefault('error_messages', {}).update(invalid=custom_message)
validator_kwarg = [ validator_kwarg = [
validator for validator in validator_kwarg validator for validator in validator_kwarg
if not isinstance(validator, validators.URLValidator) if not isinstance(validator, validators.URLValidator)
@ -162,6 +170,9 @@ def get_field_kwargs(field_name, model_field):
# EmailField does not need to include the validate_email argument, # EmailField does not need to include the validate_email argument,
# as it is explicitly added in. # as it is explicitly added in.
if isinstance(model_field, models.EmailField): if isinstance(model_field, models.EmailField):
custom_message = model_field.error_messages.get("invalid", None)
if custom_message is not None:
kwargs.setdefault('error_messages', {}).update(invalid=custom_message)
validator_kwarg = [ validator_kwarg = [
validator for validator in validator_kwarg validator for validator in validator_kwarg
if validator is not validators.validate_email if validator is not validators.validate_email
@ -194,6 +205,9 @@ def get_field_kwargs(field_name, model_field):
isinstance(model_field, models.TextField) or isinstance(model_field, models.TextField) or
isinstance(model_field, models.FileField)): isinstance(model_field, models.FileField)):
kwargs['max_length'] = max_length kwargs['max_length'] = max_length
custom_message = model_field.error_messages.get("max_length", '')
if custom_message != '':
kwargs.setdefault('error_messages', OrderedDict()).update(max_length=custom_message)
validator_kwarg = [ validator_kwarg = [
validator for validator in validator_kwarg validator for validator in validator_kwarg
if not isinstance(validator, validators.MaxLengthValidator) if not isinstance(validator, validators.MaxLengthValidator)
@ -201,12 +215,14 @@ def get_field_kwargs(field_name, model_field):
# Ensure that min_length is passed explicitly as a keyword arg, # Ensure that min_length is passed explicitly as a keyword arg,
# rather than as a validator. # rather than as a validator.
min_length = next(( min_length, message = next((
validator.limit_value for validator in validator_kwarg (validator.limit_value, validator.message) for validator in validator_kwarg
if isinstance(validator, validators.MinLengthValidator) if isinstance(validator, validators.MinLengthValidator)
), None) ), (None, ''))
if min_length is not None and isinstance(model_field, models.CharField): if min_length is not None and isinstance(model_field, models.CharField):
kwargs['min_length'] = min_length kwargs['min_length'] = min_length
if message != '':
kwargs.setdefault('error_messages', OrderedDict()).update(min_length=message)
validator_kwarg = [ validator_kwarg = [
validator for validator in validator_kwarg validator for validator in validator_kwarg
if not isinstance(validator, validators.MinLengthValidator) if not isinstance(validator, validators.MinLengthValidator)

View File

@ -200,7 +200,7 @@ class TestRegularFieldMappings(TestCase):
expected = dedent(""" expected = dedent("""
TestSerializer(): TestSerializer():
id = IntegerField(label='ID', read_only=True) id = IntegerField(label='ID', read_only=True)
value_limit_field = IntegerField(max_value=10, min_value=1) value_limit_field = IntegerField(error_messages=OrderedDict([('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_value=10, min_value=1)
length_limit_field = CharField(max_length=12, min_length=3) length_limit_field = CharField(max_length=12, min_length=3)
blank_field = CharField(allow_blank=True, max_length=10, required=False) blank_field = CharField(allow_blank=True, max_length=10, required=False)
null_field = IntegerField(allow_null=True, required=False) null_field = IntegerField(allow_null=True, required=False)
@ -214,7 +214,11 @@ class TestRegularFieldMappings(TestCase):
expected = expected.replace( expected = expected.replace(
"('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')", "('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')",
"(u'red', u'Red'), (u'blue', u'Blue'), (u'green', u'Green')" "(u'red', u'Red'), (u'blue', u'Blue'), (u'green', u'Green')"
).replace(
"{'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_value': u'Ensure this value is less than or equal to %(limit_value)s.', 'min_value': u'Ensure this value is greater than or equal to %(limit_value)s.'}"
) )
self.maxDiff = None
self.assertEqual(unicode_repr(TestSerializer()), expected) self.assertEqual(unicode_repr(TestSerializer()), expected)
def test_method_field(self): def test_method_field(self):

View File

@ -1,6 +1,9 @@
import datetime import datetime
import pytest import pytest
from django.core.validators import (
MaxValueValidator, MinLengthValidator, MinValueValidator
)
from django.db import DataError, models from django.db import DataError, models
from django.test import TestCase from django.test import TestCase
@ -563,3 +566,99 @@ class ValidatorsTests(TestCase):
date_field='bar') date_field='bar')
with pytest.raises(NotImplementedError): with pytest.raises(NotImplementedError):
validator.filter_queryset(attrs=None, queryset=None) validator.filter_queryset(attrs=None, queryset=None)
class ItemModel(models.Model):
price = models.DecimalField(decimal_places=2, max_digits=10, validators=[MinValueValidator(limit_value=0, message='Price has to be >= 0.'),
MaxValueValidator(limit_value=10, message='Price has to be <= 10.')])
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = ItemModel
fields = '__all__'
class ValidatorMessageTests(TestCase):
def test_min_value_validator_message_is_copied_from_model(self):
data = {'price': -1}
s = ItemSerializer(data=data, partial=True)
s.is_valid()
assert s.errors['price'] == ['Price has to be >= 0.']
def test_max_value_validator_message_is_copied_from_model(self):
data = {'price': 11}
s = ItemSerializer(data=data, partial=True)
s.is_valid()
assert s.errors['price'] == ['Price has to be <= 10.']
def test_url_validator_message_is_copied_from_model(self):
class BlogModel(models.Model):
url = models.URLField(
error_messages={"invalid": "This URL is not valid."}
)
class BlogSerializer(serializers.ModelSerializer):
class Meta:
model = BlogModel
fields = '__all__'
data = {'url': 'broken url'}
s = BlogSerializer(data=data)
s.is_valid()
assert s.errors['url'] == ['This URL is not valid.']
def test_email_validator_message_is_copied_from_model(self):
class UserModel(models.Model):
email = models.EmailField(
error_messages={"invalid": "Please enter a valid email."}
)
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = UserModel
fields = '__all__'
data = {'email': 'invalid email'}
s = UserSerializer(data=data)
s.is_valid()
assert s.errors['email'] == ['Please enter a valid email.']
def test_min_length_validator_message_is_copied_from_model(self):
class Review(models.Model):
text = models.CharField(
max_length=100,
validators=[MinLengthValidator(limit_value=5, message='This is too short.')]
)
class ReviewSerializer(serializers.ModelSerializer):
class Meta:
model = Review
fields = '__all__'
data = {'text': 'Hi'}
s = ReviewSerializer(data=data)
s.is_valid()
assert s.errors['text'] == ['This is too short.']
def test_max_length_validator_message_is_copied_from_model(self):
class Post(models.Model):
text = models.CharField(
max_length=1,
error_messages={"max_length": "This is too long"}
)
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
data = {'text': 'A very long text'}
s = PostSerializer(data=data)
assert not s.is_valid()
assert s.errors['text'] == ['This is too long']