mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-25 11:04:02 +03:00
min_value/max_value support in DurationField (#5643)
* Added min_value/max_value field arguments to DurationField. * Made field mapping use mix/max kwargs for DurationField validators.
This commit is contained in:
parent
7d64b7016d
commit
7268643b25
|
@ -360,7 +360,10 @@ Corresponds to `django.db.models.fields.DurationField`
|
||||||
The `validated_data` for these fields will contain a `datetime.timedelta` instance.
|
The `validated_data` for these fields will contain a `datetime.timedelta` instance.
|
||||||
The representation is a string following this format `'[DD] [HH:[MM:]]ss[.uuuuuu]'`.
|
The representation is a string following this format `'[DD] [HH:[MM:]]ss[.uuuuuu]'`.
|
||||||
|
|
||||||
**Signature:** `DurationField()`
|
**Signature:** `DurationField(max_value=None, min_value=None)`
|
||||||
|
|
||||||
|
- `max_value` Validate that the duration provided is no greater than this value.
|
||||||
|
- `min_value` Validate that the duration provided is no less than this value.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -1354,8 +1354,27 @@ class TimeField(Field):
|
||||||
class DurationField(Field):
|
class DurationField(Field):
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'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}.'),
|
||||||
|
'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.max_value = kwargs.pop('max_value', None)
|
||||||
|
self.min_value = kwargs.pop('min_value', None)
|
||||||
|
super(DurationField, self).__init__(**kwargs)
|
||||||
|
if self.max_value is not None:
|
||||||
|
message = lazy(
|
||||||
|
self.error_messages['max_value'].format,
|
||||||
|
six.text_type)(max_value=self.max_value)
|
||||||
|
self.validators.append(
|
||||||
|
MaxValueValidator(self.max_value, message=message))
|
||||||
|
if self.min_value is not None:
|
||||||
|
message = lazy(
|
||||||
|
self.error_messages['min_value'].format,
|
||||||
|
six.text_type)(min_value=self.min_value)
|
||||||
|
self.validators.append(
|
||||||
|
MinValueValidator(self.min_value, message=message))
|
||||||
|
|
||||||
def to_internal_value(self, value):
|
def to_internal_value(self, value):
|
||||||
if isinstance(value, datetime.timedelta):
|
if isinstance(value, datetime.timedelta):
|
||||||
return value
|
return value
|
||||||
|
|
|
@ -12,7 +12,7 @@ from rest_framework.compat import postgres_fields
|
||||||
from rest_framework.validators import UniqueValidator
|
from rest_framework.validators import UniqueValidator
|
||||||
|
|
||||||
NUMERIC_FIELD_TYPES = (
|
NUMERIC_FIELD_TYPES = (
|
||||||
models.IntegerField, models.FloatField, models.DecimalField
|
models.IntegerField, models.FloatField, models.DecimalField, models.DurationField,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1459,6 +1459,23 @@ class TestNoOutputFormatTimeField(FieldValues):
|
||||||
field = serializers.TimeField(format=None)
|
field = serializers.TimeField(format=None)
|
||||||
|
|
||||||
|
|
||||||
|
class TestMinMaxDurationField(FieldValues):
|
||||||
|
"""
|
||||||
|
Valid and invalid values for `DurationField` with min and max limits.
|
||||||
|
"""
|
||||||
|
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.'],
|
||||||
|
'4 08:32:01.000123': ['Ensure this value is less than or equal to 4 days, 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), max_value=datetime.timedelta(days=4))
|
||||||
|
|
||||||
|
|
||||||
class TestDurationField(FieldValues):
|
class TestDurationField(FieldValues):
|
||||||
"""
|
"""
|
||||||
Valid and invalid values for `DurationField`.
|
Valid and invalid values for `DurationField`.
|
||||||
|
|
|
@ -7,6 +7,7 @@ an appropriate set of serializer fields for each case.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
@ -16,7 +17,6 @@ from django.core.validators import (
|
||||||
MaxValueValidator, MinLengthValidator, MinValueValidator
|
MaxValueValidator, MinLengthValidator, MinValueValidator
|
||||||
)
|
)
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import DurationField as ModelDurationField
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
|
@ -349,7 +349,7 @@ class TestDurationFieldMapping(TestCase):
|
||||||
"""
|
"""
|
||||||
A model that defines DurationField.
|
A model that defines DurationField.
|
||||||
"""
|
"""
|
||||||
duration_field = ModelDurationField()
|
duration_field = models.DurationField()
|
||||||
|
|
||||||
class TestSerializer(serializers.ModelSerializer):
|
class TestSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -363,6 +363,27 @@ class TestDurationFieldMapping(TestCase):
|
||||||
""")
|
""")
|
||||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
||||||
|
|
||||||
|
def test_duration_field_with_validators(self):
|
||||||
|
class ValidatedDurationFieldModel(models.Model):
|
||||||
|
"""
|
||||||
|
A model that defines DurationField with validators.
|
||||||
|
"""
|
||||||
|
duration_field = models.DurationField(
|
||||||
|
validators=[MinValueValidator(datetime.timedelta(days=1)), MaxValueValidator(datetime.timedelta(days=3))]
|
||||||
|
)
|
||||||
|
|
||||||
|
class TestSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = ValidatedDurationFieldModel
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
expected = dedent("""
|
||||||
|
TestSerializer():
|
||||||
|
id = IntegerField(label='ID', read_only=True)
|
||||||
|
duration_field = DurationField(max_value=datetime.timedelta(3), min_value=datetime.timedelta(1))
|
||||||
|
""")
|
||||||
|
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
||||||
|
|
||||||
|
|
||||||
class TestGenericIPAddressFieldValidation(TestCase):
|
class TestGenericIPAddressFieldValidation(TestCase):
|
||||||
def test_ip_address_validation(self):
|
def test_ip_address_validation(self):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user