diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index c7a02bbb7..8648462cf 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -280,7 +280,8 @@ Corresponds to `django.db.models.fields.FloatField`. - `max_value` Validate that the number provided is no greater than this value. - `min_value` Validate that the number provided is no less than this value. -- `exclusive_min` If true, validate that the number provided is no greater than or equals `min_value`. +- `exclusive_min` If true, validate that the number provided is no greater than or equals to `min_value`. +- `exclusive_max` If true, validate that the number provided is no less than or equals to `max_value`. ## DecimalField diff --git a/rest_framework/fields.py b/rest_framework/fields.py index dd1cadc9b..6c0cc9436 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -38,7 +38,8 @@ from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.settings import api_settings from rest_framework.utils import html, humanize_datetime, json, representation from rest_framework.utils.formatting import lazy_format -from rest_framework.validators import ExclusiveLMinValueValidator, ProhibitSurrogateCharactersValidator +from rest_framework.validators import (ExclusiveMaxValueValidator, ExclusiveMinValueValidator, + ProhibitSurrogateCharactersValidator) class empty: @@ -967,6 +968,7 @@ 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}.'), + 'exclusive_max_value': _('Ensure this value is less than {max_value}.'), 'min_value': _('Ensure this value is greater than or equal to {min_value}.'), 'exclusive_min_value': _('Ensure this value is greater than {min_value}.'), 'max_string_length': _('String value too large.') @@ -977,16 +979,22 @@ class FloatField(Field): self.max_value = kwargs.pop('max_value', None) self.min_value = kwargs.pop('min_value', None) self.exclusive_min = kwargs.pop("exclusive_min", False) + self.exclusive_max = kwargs.pop("exclusive_max", False) super().__init__(**kwargs) if self.max_value is not None: - message = lazy_format(self.error_messages['max_value'], max_value=self.max_value) - self.validators.append( - MaxValueValidator(self.max_value, message=message)) + if self.exclusive_max: + message = lazy_format(self.error_messages["exclusive_max_value"], max_value=self.max_value) + self.validators.append( + ExclusiveMaxValueValidator(self.max_value, message=message)) + else: + message = lazy_format(self.error_messages['max_value'], max_value=self.max_value) + self.validators.append( + MaxValueValidator(self.max_value, message=message)) if self.min_value is not None: if self.exclusive_min: message = lazy_format(self.error_messages["exclusive_min_value"], min_value=self.min_value) self.validators.append( - ExclusiveLMinValueValidator(self.min_value, message=message)) + ExclusiveMinValueValidator(self.min_value, message=message)) else: message = lazy_format(self.error_messages['min_value'], min_value=self.min_value) self.validators.append( diff --git a/rest_framework/validators.py b/rest_framework/validators.py index 1cae3cb67..0fa027081 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -281,9 +281,17 @@ class UniqueForYearValidator(BaseUniqueForValidator): return qs_filter(queryset, **filter_kwargs) -class ExclusiveLMinValueValidator(BaseValidator): +class ExclusiveMinValueValidator(BaseValidator): message = _('Ensure this value is greater than %(limit_value)s.') code = "exclusive_min_value" def compare(self, a, b): return a <= b + + +class ExclusiveMaxValueValidator(BaseValidator): + message = _('Ensure this value is lesser than %(limit_value)s.') + code = "exclusive_max_value" + + def compare(self, a, b): + return a >= b diff --git a/tests/test_fields.py b/tests/test_fields.py index 0e2f787b6..d89062094 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1078,7 +1078,7 @@ class TestMinMaxFloatField(FieldValues): class TestExclusiveMinFloatField(FieldValues): """ - Valid and invalid values for 'FloatField' with exclusive_min limits. + Valid and invalid values for 'FloatField' with exclusive_min limit. """ valid_inputs = { '1.01': 1.01, @@ -1096,6 +1096,27 @@ class TestExclusiveMinFloatField(FieldValues): field = serializers.FloatField(min_value=1, exclusive_min=True) +class TestExclusiveMaxFloatField(FieldValues): + """ + Valid and invalid values for 'FloatField' with exclusive_max limit. + """ + valid_inputs = { + '1': 1, + '2.9': 2.9, + 1: 1, + 2.9: 2.9, + 1.0: 1.0, + } + + invalid_inputs = { + 3: ['Ensure this value is less than 3.'], + '3': ['Ensure this value is less than 3.'], + '3.0': ['Ensure this value is less than 3.'], + } + outputs = {} + field = serializers.FloatField(max_value=3, exclusive_max=True) + + class TestDecimalField(FieldValues): """ Valid and invalid values for `DecimalField`.