mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-06 21:40:13 +03:00
Merge 826fd76825
into ff556a91fd
This commit is contained in:
commit
34d052aefc
|
@ -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.'),
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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']
|
||||||
|
|
Loading…
Reference in New Issue
Block a user