mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 09:36:49 +03:00
Add DurationField
This commit is contained in:
parent
a0f66ffc69
commit
f701ecceb7
|
@ -302,6 +302,18 @@ Corresponds to `django.db.models.fields.TimeField`
|
|||
|
||||
Format strings may either be [Python strftime formats][strftime] which explicitly specify the format, or the special string `'iso-8601'`, which indicates that [ISO 8601][iso8601] style times should be used. (eg `'12:34:56.000000'`)
|
||||
|
||||
## DurationField
|
||||
|
||||
A Duration representation.
|
||||
Corresponds to `django.db.models.fields.Duration`
|
||||
|
||||
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]'`.
|
||||
|
||||
**Note:** This field is only available with Django versions >= 1.8.
|
||||
|
||||
**Signature:** `DurationField()`
|
||||
|
||||
---
|
||||
|
||||
# Choice selection fields
|
||||
|
|
|
@ -258,3 +258,11 @@ else:
|
|||
SHORT_SEPARATORS = (b',', b':')
|
||||
LONG_SEPARATORS = (b', ', b': ')
|
||||
INDENT_SEPARATORS = (b',', b': ')
|
||||
|
||||
|
||||
if django.VERSION >= (1, 8):
|
||||
from django.db.models import DurationField
|
||||
from django.utils.dateparse import parse_duration
|
||||
from django.utils.duration import duration_string
|
||||
else:
|
||||
DurationField = duration_string = parse_duration = None
|
||||
|
|
|
@ -12,7 +12,7 @@ from rest_framework import ISO_8601
|
|||
from rest_framework.compat import (
|
||||
EmailValidator, MinValueValidator, MaxValueValidator,
|
||||
MinLengthValidator, MaxLengthValidator, URLValidator, OrderedDict,
|
||||
unicode_repr, unicode_to_repr
|
||||
unicode_repr, unicode_to_repr, parse_duration, duration_string,
|
||||
)
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.settings import api_settings
|
||||
|
@ -1003,6 +1003,29 @@ class TimeField(Field):
|
|||
return value.strftime(self.format)
|
||||
|
||||
|
||||
class DurationField(Field):
|
||||
default_error_messages = {
|
||||
'invalid': _('Duration has wrong format. Use one of these formats instead: {format}.'),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if parse_duration is None:
|
||||
raise NotImplementedError(
|
||||
'DurationField not supported for django versions prior to 1.8')
|
||||
return super(DurationField, self).__init__(*args, **kwargs)
|
||||
|
||||
def to_internal_value(self, value):
|
||||
if isinstance(value, datetime.timedelta):
|
||||
return value
|
||||
parsed = parse_duration(value)
|
||||
if parsed is not None:
|
||||
return parsed
|
||||
self.fail('invalid', format='[DD] [HH:[MM:]]ss[.uuuuuu]')
|
||||
|
||||
def to_representation(self, value):
|
||||
return duration_string(value)
|
||||
|
||||
|
||||
# Choice types...
|
||||
|
||||
class ChoiceField(Field):
|
||||
|
|
|
@ -15,7 +15,11 @@ from django.db import models
|
|||
from django.db.models.fields import FieldDoesNotExist, Field as DjangoModelField
|
||||
from django.db.models import query
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework.compat import postgres_fields, unicode_to_repr
|
||||
from rest_framework.compat import (
|
||||
postgres_fields,
|
||||
unicode_to_repr,
|
||||
DurationField as ModelDurationField,
|
||||
)
|
||||
from rest_framework.utils import model_meta
|
||||
from rest_framework.utils.field_mapping import (
|
||||
get_url_kwargs, get_field_kwargs,
|
||||
|
@ -731,6 +735,8 @@ class ModelSerializer(Serializer):
|
|||
models.TimeField: TimeField,
|
||||
models.URLField: URLField,
|
||||
}
|
||||
if ModelDurationField is not None:
|
||||
serializer_field_mapping[ModelDurationField] = DurationField
|
||||
serializer_related_field = PrimaryKeyRelatedField
|
||||
serializer_url_field = HyperlinkedIdentityField
|
||||
serializer_choice_field = ChoiceField
|
||||
|
|
|
@ -905,6 +905,29 @@ class TestNoOutputFormatTimeField(FieldValues):
|
|||
field = serializers.TimeField(format=None)
|
||||
|
||||
|
||||
@pytest.mark.skipif(django.VERSION < (1, 8),
|
||||
reason='DurationField is only available for django1.8+')
|
||||
class TestDurationField(FieldValues):
|
||||
"""
|
||||
Valid and invalid values for `DurationField`.
|
||||
"""
|
||||
valid_inputs = {
|
||||
'13': datetime.timedelta(seconds=13),
|
||||
'3 08:32:01.000123': datetime.timedelta(days=3, hours=8, minutes=32, seconds=1, microseconds=123),
|
||||
'08:01': datetime.timedelta(minutes=8, seconds=1),
|
||||
datetime.timedelta(days=3, hours=8, minutes=32, seconds=1, microseconds=123): datetime.timedelta(days=3, hours=8, minutes=32, seconds=1, microseconds=123),
|
||||
}
|
||||
invalid_inputs = {
|
||||
'abc': ['Duration has wrong format. Use one of these formats instead: [DD] [HH:[MM:]]ss[.uuuuuu].'],
|
||||
'3 08:32 01.123': ['Duration has wrong format. Use one of these formats instead: [DD] [HH:[MM:]]ss[.uuuuuu].'],
|
||||
}
|
||||
outputs = {
|
||||
datetime.timedelta(days=3, hours=8, minutes=32, seconds=1, microseconds=123): '3 08:32:01.000123',
|
||||
}
|
||||
if django.VERSION >= (1, 8):
|
||||
field = serializers.DurationField()
|
||||
|
||||
|
||||
# Choice types...
|
||||
|
||||
class TestChoiceField(FieldValues):
|
||||
|
|
|
@ -6,13 +6,15 @@ These tests deal with ensuring that we correctly map the model fields onto
|
|||
an appropriate set of serializer fields for each case.
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
import django
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator, MinLengthValidator
|
||||
from django.db import models
|
||||
from django.test import TestCase
|
||||
from django.utils import six
|
||||
import pytest
|
||||
from rest_framework import serializers
|
||||
from rest_framework.compat import unicode_repr
|
||||
from rest_framework.compat import unicode_repr, DurationField as ModelDurationField
|
||||
|
||||
|
||||
def dedent(blocktext):
|
||||
|
@ -284,6 +286,28 @@ class TestRegularFieldMappings(TestCase):
|
|||
ChildSerializer().fields
|
||||
|
||||
|
||||
@pytest.mark.skipif(django.VERSION < (1, 8),
|
||||
reason='DurationField is only available for django1.8+')
|
||||
class TestDurationFieldMapping(TestCase):
|
||||
def test_duration_field(self):
|
||||
class DurationFieldModel(models.Model):
|
||||
"""
|
||||
A model that defines DurationField.
|
||||
"""
|
||||
duration_field = ModelDurationField()
|
||||
|
||||
class TestSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = DurationFieldModel
|
||||
|
||||
expected = dedent("""
|
||||
TestSerializer():
|
||||
id = IntegerField(label='ID', read_only=True)
|
||||
duration_field = DurationField()
|
||||
""")
|
||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
||||
|
||||
|
||||
# Tests for relational field mappings.
|
||||
# ------------------------------------
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user