This commit is contained in:
Sergey Piskunov 2022-08-26 14:01:32 +02:00 committed by GitHub
commit c9849e8a02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 253 additions and 37 deletions

View File

@ -901,6 +901,7 @@ class IntegerField(Field):
'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 {max_value}.'),
'min_value': _('Ensure this value is greater than or equal to {min_value}.'), 'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
'range': _('Ensure this value is in the range between {min_value} and {max_value}.'),
'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.
@ -910,14 +911,22 @@ class IntegerField(Field):
self.max_value = kwargs.pop('max_value', None) self.max_value = kwargs.pop('max_value', None)
self.min_value = kwargs.pop('min_value', None) self.min_value = kwargs.pop('min_value', None)
super().__init__(**kwargs) super().__init__(**kwargs)
if self.min_value is not None and self.max_value is not None:
message = lazy_format(
self.error_messages['range'], min_value=self.min_value, max_value=self.max_value)
else:
message = None
if self.max_value is not None: if self.max_value is not None:
message = lazy_format(self.error_messages['max_value'], max_value=self.max_value)
self.validators.append( self.validators.append(
MaxValueValidator(self.max_value, message=message)) MaxValueValidator(self.max_value, message=message or lazy_format(
self.error_messages['max_value'], max_value=self.max_value)))
if self.min_value is not None: if self.min_value is not None:
message = lazy_format(self.error_messages['min_value'], min_value=self.min_value)
self.validators.append( self.validators.append(
MinValueValidator(self.min_value, message=message)) MinValueValidator(self.min_value, message=message or lazy_format(
self.error_messages['min_value'], min_value=self.min_value)))
def to_internal_value(self, data): def to_internal_value(self, data):
if isinstance(data, str) and len(data) > self.MAX_STRING_LENGTH: if isinstance(data, str) and len(data) > self.MAX_STRING_LENGTH:
@ -938,6 +947,7 @@ class FloatField(Field):
'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 {max_value}.'),
'min_value': _('Ensure this value is greater than or equal to {min_value}.'), 'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
'range': _('Ensure this value is in the range between {min_value} and {max_value}.'),
'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.
@ -946,14 +956,22 @@ class FloatField(Field):
self.max_value = kwargs.pop('max_value', None) self.max_value = kwargs.pop('max_value', None)
self.min_value = kwargs.pop('min_value', None) self.min_value = kwargs.pop('min_value', None)
super().__init__(**kwargs) super().__init__(**kwargs)
if self.min_value is not None and self.max_value is not None:
message = lazy_format(
self.error_messages['range'], min_value=self.min_value, max_value=self.max_value)
else:
message = None
if self.max_value is not None: if self.max_value is not None:
message = lazy_format(self.error_messages['max_value'], max_value=self.max_value)
self.validators.append( self.validators.append(
MaxValueValidator(self.max_value, message=message)) MaxValueValidator(self.max_value, message=message or lazy_format(
self.error_messages['max_value'], max_value=self.max_value)))
if self.min_value is not None: if self.min_value is not None:
message = lazy_format(self.error_messages['min_value'], min_value=self.min_value)
self.validators.append( self.validators.append(
MinValueValidator(self.min_value, message=message)) MinValueValidator(self.min_value, message=message or lazy_format(
self.error_messages['min_value'], min_value=self.min_value)))
def to_internal_value(self, data): def to_internal_value(self, data):
@ -974,6 +992,7 @@ class DecimalField(Field):
'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 {max_value}.'),
'min_value': _('Ensure this value is greater than or equal to {min_value}.'), 'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
'range': _('Ensure this value is in the range between {min_value} and {max_value}.'),
'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.'),
@ -1001,14 +1020,21 @@ class DecimalField(Field):
super().__init__(**kwargs) super().__init__(**kwargs)
if self.min_value is not None and self.max_value is not None:
message = lazy_format(
self.error_messages['range'], min_value=self.min_value, max_value=self.max_value)
else:
message = None
if self.max_value is not None: if self.max_value is not None:
message = lazy_format(self.error_messages['max_value'], max_value=self.max_value)
self.validators.append( self.validators.append(
MaxValueValidator(self.max_value, message=message)) MaxValueValidator(self.max_value, message=message or lazy_format(
self.error_messages['max_value'], max_value=self.max_value)))
if self.min_value is not None: if self.min_value is not None:
message = lazy_format(self.error_messages['min_value'], min_value=self.min_value)
self.validators.append( self.validators.append(
MinValueValidator(self.min_value, message=message)) MinValueValidator(self.min_value, message=message or lazy_format(
self.error_messages['min_value'], min_value=self.min_value)))
if rounding is not None: if rounding is not None:
valid_roundings = [v for k, v in vars(decimal).items() if k.startswith('ROUND_')] valid_roundings = [v for k, v in vars(decimal).items() if k.startswith('ROUND_')]
@ -1346,20 +1372,29 @@ class DurationField(Field):
'invalid': _('Duration has wrong format. Use one of these formats instead: {format}.'), 'invalid': _('Duration has wrong format. Use one of these formats instead: {format}.'),
'max_value': _('Ensure this value is less than or equal to {max_value}.'), 'max_value': _('Ensure this value is less than or equal to {max_value}.'),
'min_value': _('Ensure this value is greater than or equal to {min_value}.'), 'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
'range': _('Ensure this value is in the range between {min_value} and {max_value}.'),
} }
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.max_value = kwargs.pop('max_value', None) self.max_value = kwargs.pop('max_value', None)
self.min_value = kwargs.pop('min_value', None) self.min_value = kwargs.pop('min_value', None)
super().__init__(**kwargs) super().__init__(**kwargs)
if self.min_value is not None and self.max_value is not None:
message = lazy_format(
self.error_messages['range'], min_value=self.min_value, max_value=self.max_value)
else:
message = None
if self.max_value is not None: if self.max_value is not None:
message = lazy_format(self.error_messages['max_value'], max_value=self.max_value)
self.validators.append( self.validators.append(
MaxValueValidator(self.max_value, message=message)) MaxValueValidator(self.max_value, message=message or lazy_format(
self.error_messages['max_value'], max_value=self.max_value)))
if self.min_value is not None: if self.min_value is not None:
message = lazy_format(self.error_messages['min_value'], min_value=self.min_value)
self.validators.append( self.validators.append(
MinValueValidator(self.min_value, message=message)) MinValueValidator(self.min_value, message=message or lazy_format(
self.error_messages['min_value'], min_value=self.min_value)))
def to_internal_value(self, value): def to_internal_value(self, value):
if isinstance(value, datetime.timedelta): if isinstance(value, datetime.timedelta):
@ -1586,7 +1621,8 @@ class ListField(Field):
'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}".'),
'empty': _('This list may not be empty.'), 'empty': _('This list may not be empty.'),
'min_length': _('Ensure this field has at least {min_length} elements.'), 'min_length': _('Ensure this field has at least {min_length} elements.'),
'max_length': _('Ensure this field has no more than {max_length} elements.') 'max_length': _('Ensure this field has no more than {max_length} elements.'),
'range': _('Ensure this field has at least {min_length} and no more than {max_length} elements.'),
} }
def __init__(self, **kwargs): def __init__(self, **kwargs):
@ -1603,12 +1639,22 @@ class ListField(Field):
super().__init__(**kwargs) super().__init__(**kwargs)
self.child.bind(field_name='', parent=self) self.child.bind(field_name='', parent=self)
if self.min_length is not None and self.max_length is not None:
message = lazy_format(
self.error_messages['range'], min_length=self.min_length, max_length=self.max_length)
else:
message = None
if self.max_length is not None: if self.max_length is not None:
message = lazy_format(self.error_messages['max_length'], max_length=self.max_length) self.validators.append(
self.validators.append(MaxLengthValidator(self.max_length, message=message)) MaxLengthValidator(self.max_length, message=message or lazy_format(
self.error_messages['max_length'], max_length=self.max_length)))
if self.min_length is not None: if self.min_length is not None:
message = lazy_format(self.error_messages['min_length'], min_length=self.min_length) self.validators.append(
self.validators.append(MinLengthValidator(self.min_length, message=message)) MinLengthValidator(self.min_length, message=message or lazy_format(
self.error_messages['min_length'], min_length=self.min_length)))
def get_value(self, dictionary): def get_value(self, dictionary):
if self.field_name not in dictionary: if self.field_name not in dictionary:

View File

@ -1025,15 +1025,51 @@ class TestMinMaxIntegerField(FieldValues):
3: 3, 3: 3,
} }
invalid_inputs = { invalid_inputs = {
0: ['Ensure this value is greater than or equal to 1.'], 0: ['Ensure this value is in the range between 1 and 3.'],
4: ['Ensure this value is less than or equal to 3.'], 4: ['Ensure this value is in the range between 1 and 3.'],
'0': ['Ensure this value is greater than or equal to 1.'], '0': ['Ensure this value is in the range between 1 and 3.'],
'4': ['Ensure this value is less than or equal to 3.'], '4': ['Ensure this value is in the range between 1 and 3.'],
} }
outputs = {} outputs = {}
field = serializers.IntegerField(min_value=1, max_value=3) field = serializers.IntegerField(min_value=1, max_value=3)
class TestMinIntegerField(FieldValues):
"""
Valid and invalid values for `IntegerField` with min limit.
"""
valid_inputs = {
'1': 1,
'3': 3,
1: 1,
3: 3,
}
invalid_inputs = {
0: ['Ensure this value is greater than or equal to 1.'],
'0': ['Ensure this value is greater than or equal to 1.'],
}
outputs = {}
field = serializers.IntegerField(min_value=1)
class TestMaxIntegerField(FieldValues):
"""
Valid and invalid values for `IntegerField` with max limit.
"""
valid_inputs = {
'1': 1,
'3': 3,
1: 1,
3: 3,
}
invalid_inputs = {
4: ['Ensure this value is less than or equal to 3.'],
'4': ['Ensure this value is less than or equal to 3.'],
}
outputs = {}
field = serializers.IntegerField(max_value=3)
class TestFloatField(FieldValues): class TestFloatField(FieldValues):
""" """
Valid and invalid values for `FloatField`. Valid and invalid values for `FloatField`.
@ -1073,15 +1109,55 @@ class TestMinMaxFloatField(FieldValues):
3.0: 3.0, 3.0: 3.0,
} }
invalid_inputs = { invalid_inputs = {
0.9: ['Ensure this value is greater than or equal to 1.'], 0.9: ['Ensure this value is in the range between 1 and 3.'],
3.1: ['Ensure this value is less than or equal to 3.'], 3.1: ['Ensure this value is in the range between 1 and 3.'],
'0.0': ['Ensure this value is greater than or equal to 1.'], '0.0': ['Ensure this value is in the range between 1 and 3.'],
'3.1': ['Ensure this value is less than or equal to 3.'], '3.1': ['Ensure this value is in the range between 1 and 3.'],
} }
outputs = {} outputs = {}
field = serializers.FloatField(min_value=1, max_value=3) field = serializers.FloatField(min_value=1, max_value=3)
class TestMinFloatField(FieldValues):
"""
Valid and invalid values for `FloatField` with min limit.
"""
valid_inputs = {
'1': 1,
'3': 3,
1: 1,
3: 3,
1.0: 1.0,
3.0: 3.0,
}
invalid_inputs = {
0.9: ['Ensure this value is greater than or equal to 1.'],
'0.0': ['Ensure this value is greater than or equal to 1.'],
}
outputs = {}
field = serializers.FloatField(min_value=1)
class TestMaxFloatField(FieldValues):
"""
Valid and invalid values for `FloatField` with max limit.
"""
valid_inputs = {
'1': 1,
'3': 3,
1: 1,
3: 3,
1.0: 1.0,
3.0: 3.0,
}
invalid_inputs = {
3.1: ['Ensure this value is less than or equal to 3.'],
'3.1': ['Ensure this value is less than or equal to 3.'],
}
outputs = {}
field = serializers.FloatField(max_value=3)
class TestDecimalField(FieldValues): class TestDecimalField(FieldValues):
""" """
Valid and invalid values for `DecimalField`. Valid and invalid values for `DecimalField`.
@ -1159,8 +1235,8 @@ class TestMinMaxDecimalField(FieldValues):
'20.0': Decimal('20.0'), '20.0': Decimal('20.0'),
} }
invalid_inputs = { invalid_inputs = {
'9.9': ['Ensure this value is greater than or equal to 10.'], '9.9': ['Ensure this value is in the range between 10 and 20.'],
'20.1': ['Ensure this value is less than or equal to 20.'], '20.1': ['Ensure this value is in the range between 10 and 20.'],
} }
outputs = {} outputs = {}
field = serializers.DecimalField( field = serializers.DecimalField(
@ -1169,6 +1245,42 @@ class TestMinMaxDecimalField(FieldValues):
) )
class TestMinDecimalField(FieldValues):
"""
Valid and invalid values for `DecimalField` with min limit.
"""
valid_inputs = {
'10.0': Decimal('10.0'),
'20.0': Decimal('20.0'),
}
invalid_inputs = {
'9.9': ['Ensure this value is greater than or equal to 10.'],
}
outputs = {}
field = serializers.DecimalField(
max_digits=3, decimal_places=1,
min_value=10
)
class TestMaxDecimalField(FieldValues):
"""
Valid and invalid values for `DecimalField` with max limit.
"""
valid_inputs = {
'10.0': Decimal('10.0'),
'20.0': Decimal('20.0'),
}
invalid_inputs = {
'20.1': ['Ensure this value is less than or equal to 20.'],
}
outputs = {}
field = serializers.DecimalField(
max_digits=3, decimal_places=1,
max_value=20
)
class TestAllowEmptyStrDecimalFieldWithValidators(FieldValues): class TestAllowEmptyStrDecimalFieldWithValidators(FieldValues):
""" """
Check that empty string ('', ' ') is acceptable value for the DecimalField Check that empty string ('', ' ') is acceptable value for the DecimalField
@ -1604,14 +1716,45 @@ class TestMinMaxDurationField(FieldValues):
86401: datetime.timedelta(days=1, seconds=1), 86401: datetime.timedelta(days=1, seconds=1),
} }
invalid_inputs = { invalid_inputs = {
3600: ['Ensure this value is greater than or equal to 1 day, 0:00:00.'], 3600: ['Ensure this value is in the range between 1 day, 0:00:00 and 4 days, 0:00:00.'],
'4 08:32:01.000123': ['Ensure this value is less than or equal to 4 days, 0:00:00.'], '4 08:32:01.000123': ['Ensure this value is in the range between 1 day, 0:00:00 and 4 days, 0:00:00.'],
'3600': ['Ensure this value is greater than or equal to 1 day, 0:00:00.'], '3600': ['Ensure this value is in the range between 1 day, 0:00:00 and 4 days, 0:00:00.'],
} }
outputs = {} outputs = {}
field = serializers.DurationField(min_value=datetime.timedelta(days=1), max_value=datetime.timedelta(days=4)) field = serializers.DurationField(min_value=datetime.timedelta(days=1), max_value=datetime.timedelta(days=4))
class TestMinDurationField(FieldValues):
"""
Valid and invalid values for `DurationField` with min limit.
"""
valid_inputs = {
'3 08:32:01.000123': datetime.timedelta(days=3, hours=8, minutes=32, seconds=1, microseconds=123),
86401: datetime.timedelta(days=1, seconds=1),
}
invalid_inputs = {
3600: ['Ensure this value is greater than or equal to 1 day, 0:00:00.'],
'3600': ['Ensure this value is greater than or equal to 1 day, 0:00:00.'],
}
outputs = {}
field = serializers.DurationField(min_value=datetime.timedelta(days=1))
class TestMaxDurationField(FieldValues):
"""
Valid and invalid values for `DurationField` with max limit.
"""
valid_inputs = {
'3 08:32:01.000123': datetime.timedelta(days=3, hours=8, minutes=32, seconds=1, microseconds=123),
86401: datetime.timedelta(days=1, seconds=1),
}
invalid_inputs = {
'4 08:32:01.000123': ['Ensure this value is less than or equal to 4 days, 0:00:00.'],
}
outputs = {}
field = serializers.DurationField(max_value=datetime.timedelta(days=4))
class TestDurationField(FieldValues): class TestDurationField(FieldValues):
""" """
Valid and invalid values for `DurationField`. Valid and invalid values for `DurationField`.
@ -2062,16 +2205,43 @@ class TestEmptyListField(FieldValues):
field = serializers.ListField(child=serializers.IntegerField(), allow_empty=False) field = serializers.ListField(child=serializers.IntegerField(), allow_empty=False)
class TestListFieldLengthLimit(FieldValues): class TestListFieldMinMaxLengthLimit(FieldValues):
"""
Valid and invalid values for `ListField` with min and max limits.
"""
valid_inputs = () valid_inputs = ()
invalid_inputs = [ invalid_inputs = [
((0, 1), ['Ensure this field has at least 3 elements.']), ((0, 1), ['Ensure this field has at least 3 and no more than 4 elements.']),
((0, 1, 2, 3, 4, 5), ['Ensure this field has no more than 4 elements.']), ((0, 1, 2, 3, 4, 5), ['Ensure this field has at least 3 and no more than 4 elements.']),
] ]
outputs = () outputs = ()
field = serializers.ListField(child=serializers.IntegerField(), min_length=3, max_length=4) field = serializers.ListField(child=serializers.IntegerField(), min_length=3, max_length=4)
class TestListFieldMinLengthLimit(FieldValues):
"""
Valid and invalid values for `ListField` with min limit.
"""
valid_inputs = ()
invalid_inputs = [
((0, 1), ['Ensure this field has at least 3 elements.']),
]
outputs = ()
field = serializers.ListField(child=serializers.IntegerField(), min_length=3)
class TestListFieldMaxLengthLimit(FieldValues):
"""
Valid and invalid values for `ListField` with max limit.
"""
valid_inputs = ()
invalid_inputs = [
((0, 1, 2, 3, 4, 5), ['Ensure this field has no more than 4 elements.']),
]
outputs = ()
field = serializers.ListField(child=serializers.IntegerField(), max_length=4)
class TestUnvalidatedListField(FieldValues): class TestUnvalidatedListField(FieldValues):
""" """
Values for `ListField` with no `child` argument. Values for `ListField` with no `child` argument.