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:
Noam 2018-04-24 10:24:05 +03:00 committed by Carlton Gibson
parent 7d64b7016d
commit 7268643b25
5 changed files with 64 additions and 4 deletions

View File

@ -360,7 +360,10 @@ Corresponds to `django.db.models.fields.DurationField`
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]'`.
**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.
---

View File

@ -1354,8 +1354,27 @@ class TimeField(Field):
class DurationField(Field):
default_error_messages = {
'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):
if isinstance(value, datetime.timedelta):
return value

View File

@ -12,7 +12,7 @@ from rest_framework.compat import postgres_fields
from rest_framework.validators import UniqueValidator
NUMERIC_FIELD_TYPES = (
models.IntegerField, models.FloatField, models.DecimalField
models.IntegerField, models.FloatField, models.DecimalField, models.DurationField,
)

View File

@ -1459,6 +1459,23 @@ class TestNoOutputFormatTimeField(FieldValues):
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):
"""
Valid and invalid values for `DurationField`.

View File

@ -7,6 +7,7 @@ an appropriate set of serializer fields for each case.
"""
from __future__ import unicode_literals
import datetime
import decimal
from collections import OrderedDict
@ -16,7 +17,6 @@ from django.core.validators import (
MaxValueValidator, MinLengthValidator, MinValueValidator
)
from django.db import models
from django.db.models import DurationField as ModelDurationField
from django.test import TestCase
from django.utils import six
@ -349,7 +349,7 @@ class TestDurationFieldMapping(TestCase):
"""
A model that defines DurationField.
"""
duration_field = ModelDurationField()
duration_field = models.DurationField()
class TestSerializer(serializers.ModelSerializer):
class Meta:
@ -363,6 +363,27 @@ class TestDurationFieldMapping(TestCase):
""")
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):
def test_ip_address_validation(self):