mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-04 12:30:11 +03:00
first version of fix only for min and max value
This commit is contained in:
parent
f6c19e5eac
commit
fcc8f85664
|
@ -967,8 +967,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.'),
|
||||||
|
@ -997,11 +997,9 @@ class DecimalField(Field):
|
||||||
super(DecimalField, self).__init__(**kwargs)
|
super(DecimalField, self).__init__(**kwargs)
|
||||||
|
|
||||||
if self.max_value is not None:
|
if self.max_value is not None:
|
||||||
message = self.error_messages['max_value'].format(max_value=self.max_value)
|
self.validators.append(MaxValueValidator(self.max_value, message=self.error_messages['max_value']))
|
||||||
self.validators.append(MaxValueValidator(self.max_value, message=message))
|
|
||||||
if self.min_value is not None:
|
if self.min_value is not None:
|
||||||
message = self.error_messages['min_value'].format(min_value=self.min_value)
|
self.validators.append(MinValueValidator(self.min_value, message=self.error_messages['min_value']))
|
||||||
self.validators.append(MinValueValidator(self.min_value, message=message))
|
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -127,12 +127,13 @@ 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, messsage = 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, 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
|
||||||
|
kwargs['error_messages'] = {'max_value': messsage}
|
||||||
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 +141,13 @@ 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, messsage = 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, 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
|
||||||
|
kwargs['error_messages'] = {**kwargs['error_messages'], **{'min_value': messsage}}
|
||||||
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)
|
||||||
|
|
|
@ -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={'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)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||||
from django.db import DataError, models
|
from django.db import DataError, models
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
|
@ -245,10 +246,12 @@ class TestUniquenessTogetherValidation(TestCase):
|
||||||
When model fields are not included in a serializer, then uniqueness
|
When model fields are not included in a serializer, then uniqueness
|
||||||
validators should not be added for that field.
|
validators should not be added for that field.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class ExcludedFieldSerializer(serializers.ModelSerializer):
|
class ExcludedFieldSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = UniquenessTogetherModel
|
model = UniquenessTogetherModel
|
||||||
fields = ('id', 'race_name',)
|
fields = ('id', 'race_name',)
|
||||||
|
|
||||||
serializer = ExcludedFieldSerializer()
|
serializer = ExcludedFieldSerializer()
|
||||||
expected = dedent("""
|
expected = dedent("""
|
||||||
ExcludedFieldSerializer():
|
ExcludedFieldSerializer():
|
||||||
|
@ -262,6 +265,7 @@ class TestUniquenessTogetherValidation(TestCase):
|
||||||
When serializer fields are read only, then uniqueness
|
When serializer fields are read only, then uniqueness
|
||||||
validators should not be added for that field.
|
validators should not be added for that field.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class ReadOnlyFieldSerializer(serializers.ModelSerializer):
|
class ReadOnlyFieldSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = UniquenessTogetherModel
|
model = UniquenessTogetherModel
|
||||||
|
@ -281,6 +285,7 @@ class TestUniquenessTogetherValidation(TestCase):
|
||||||
"""
|
"""
|
||||||
Ensure validators can be explicitly removed..
|
Ensure validators can be explicitly removed..
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class NoValidatorsSerializer(serializers.ModelSerializer):
|
class NoValidatorsSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = UniquenessTogetherModel
|
model = UniquenessTogetherModel
|
||||||
|
@ -329,6 +334,7 @@ class TestUniquenessTogetherValidation(TestCase):
|
||||||
filter_queryset should add value from existing instance attribute
|
filter_queryset should add value from existing instance attribute
|
||||||
if it is not provided in attributes dict
|
if it is not provided in attributes dict
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class MockQueryset(object):
|
class MockQueryset(object):
|
||||||
def filter(self, **kwargs):
|
def filter(self, **kwargs):
|
||||||
self.called_with = kwargs
|
self.called_with = kwargs
|
||||||
|
@ -411,6 +417,7 @@ class TestUniquenessForDateValidation(TestCase):
|
||||||
'published': datetime.date(2000, 1, 1)
|
'published': datetime.date(2000, 1, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Tests for `UniqueForMonthValidator`
|
# Tests for `UniqueForMonthValidator`
|
||||||
# ----------------------------------
|
# ----------------------------------
|
||||||
|
|
||||||
|
@ -427,7 +434,6 @@ class UniqueForMonthSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class UniqueForMonthTests(TestCase):
|
class UniqueForMonthTests(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.instance = UniqueForMonthModel.objects.create(
|
self.instance = UniqueForMonthModel.objects.create(
|
||||||
slug='existing', published='2017-01-01'
|
slug='existing', published='2017-01-01'
|
||||||
|
@ -450,6 +456,7 @@ class UniqueForMonthTests(TestCase):
|
||||||
'published': datetime.date(2017, 2, 1)
|
'published': datetime.date(2017, 2, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Tests for `UniqueForYearValidator`
|
# Tests for `UniqueForYearValidator`
|
||||||
# ----------------------------------
|
# ----------------------------------
|
||||||
|
|
||||||
|
@ -466,7 +473,6 @@ class UniqueForYearSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class UniqueForYearTests(TestCase):
|
class UniqueForYearTests(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.instance = UniqueForYearModel.objects.create(
|
self.instance = UniqueForYearModel.objects.create(
|
||||||
slug='existing', published='2017-01-01'
|
slug='existing', published='2017-01-01'
|
||||||
|
@ -532,23 +538,25 @@ class TestHiddenFieldUniquenessForDateValidation(TestCase):
|
||||||
|
|
||||||
|
|
||||||
class ValidatorsTests(TestCase):
|
class ValidatorsTests(TestCase):
|
||||||
|
|
||||||
def test_qs_exists_handles_type_error(self):
|
def test_qs_exists_handles_type_error(self):
|
||||||
class TypeErrorQueryset(object):
|
class TypeErrorQueryset(object):
|
||||||
def exists(self):
|
def exists(self):
|
||||||
raise TypeError
|
raise TypeError
|
||||||
|
|
||||||
assert qs_exists(TypeErrorQueryset()) is False
|
assert qs_exists(TypeErrorQueryset()) is False
|
||||||
|
|
||||||
def test_qs_exists_handles_value_error(self):
|
def test_qs_exists_handles_value_error(self):
|
||||||
class ValueErrorQueryset(object):
|
class ValueErrorQueryset(object):
|
||||||
def exists(self):
|
def exists(self):
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
assert qs_exists(ValueErrorQueryset()) is False
|
assert qs_exists(ValueErrorQueryset()) is False
|
||||||
|
|
||||||
def test_qs_exists_handles_data_error(self):
|
def test_qs_exists_handles_data_error(self):
|
||||||
class DataErrorQueryset(object):
|
class DataErrorQueryset(object):
|
||||||
def exists(self):
|
def exists(self):
|
||||||
raise DataError
|
raise DataError
|
||||||
|
|
||||||
assert qs_exists(DataErrorQueryset()) is False
|
assert qs_exists(DataErrorQueryset()) is False
|
||||||
|
|
||||||
def test_validator_raises_error_if_not_all_fields_are_provided(self):
|
def test_validator_raises_error_if_not_all_fields_are_provided(self):
|
||||||
|
@ -563,3 +571,30 @@ 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.']
|
||||||
|
|
Loading…
Reference in New Issue
Block a user