Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions docs/api-guide/fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -377,13 +377,16 @@ A Duration representation.
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(max_value=None, min_value=None)`
**Signature:** `DurationField(format=api_settings.DURATION_FORMAT, max_value=None, min_value=None)`

* `format` - A string representing the output format. If not specified, this defaults to the same value as the `DURATION_FORMAT` settings key, which will be `'standard'` unless set. Setting to a format string indicates that `to_representation` return values should be coerced to string output. Format strings are described below. Setting this value to `None` indicates that Python `timedelta` objects should be returned by `to_representation`. In this case the date encoding will be determined by the renderer.
* `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.

#### `DurationField` format strings
Format strings may either be the special string `'iso-8601'`, which indicates that [ISO 8601][iso8601] style intervals should be used (eg `'P4DT1H15M20S'`), or the special string `'standard'`, which indicates that Django interval format `'[DD] [HH:[MM:]]ss[.uuuuuu]'` should be used (eg: `'4 1:15:20'`).
Comment thread
peterthomassen marked this conversation as resolved.
Outdated
Comment thread
peterthomassen marked this conversation as resolved.
Outdated

---

# Choice selection fields
Expand Down
9 changes: 9 additions & 0 deletions docs/api-guide/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,15 @@ May be a list including the string `'iso-8601'` or Python [strftime format][strf

Default: `['iso-8601']`


#### DURATION_FORMAT

A format string that should be used by default for rendering the output of `DurationField` serializer fields. If `None`, then `DurationField` serializer fields will return Python `timedelta` objects, and the duration encoding will be determined by the renderer.
Comment thread
peterthomassen marked this conversation as resolved.
Outdated
Comment thread
peterthomassen marked this conversation as resolved.
Outdated

May be any of `None`, `'iso-8601'` or `'standard'` (the format accepted by `django.utils.dateparse.parse_duration`).

Default: `'standard'`
Comment thread
peterthomassen marked this conversation as resolved.
Outdated

---

## Encodings
Expand Down
1 change: 1 addition & 0 deletions rest_framework/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

# Default datetime input and output formats
ISO_8601 = 'iso-8601'
STD_DURATION = 'standard'
Comment thread
peterthomassen marked this conversation as resolved.
Outdated


class RemovedInDRF317Warning(PendingDeprecationWarning):
Expand Down
13 changes: 11 additions & 2 deletions rest_framework/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from django.utils.dateparse import (
parse_date, parse_datetime, parse_duration, parse_time
)
from django.utils.duration import duration_string
from django.utils.duration import duration_iso_string, duration_string
from django.utils.encoding import is_protected_type, smart_str
from django.utils.formats import localize_input, sanitize_separators
from django.utils.ipv6 import clean_ipv6_address
Expand Down Expand Up @@ -1351,9 +1351,11 @@ class DurationField(Field):
'overflow': _('The number of days must be between {min_days} and {max_days}.'),
}

def __init__(self, **kwargs):
def __init__(self, format=empty, **kwargs):
Comment thread
peterthomassen marked this conversation as resolved.
Outdated
self.max_value = kwargs.pop('max_value', None)
self.min_value = kwargs.pop('min_value', None)
if format is not empty:
self.format = format
super().__init__(**kwargs)
if self.max_value is not None:
message = lazy_format(self.error_messages['max_value'], max_value=self.max_value)
Expand All @@ -1376,6 +1378,13 @@ def to_internal_value(self, value):
self.fail('invalid', format='[DD] [HH:[MM:]]ss[.uuuuuu]')
Comment thread
browniebroke marked this conversation as resolved.

def to_representation(self, value):
output_format = getattr(self, 'format', api_settings.DURATION_FORMAT)
Comment thread
browniebroke marked this conversation as resolved.

if output_format is None or isinstance(value, str):
return value

if output_format.lower() == ISO_8601:
return duration_iso_string(value)
return duration_string(value)


Expand Down
4 changes: 3 additions & 1 deletion rest_framework/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from django.core.signals import setting_changed
from django.utils.module_loading import import_string

from rest_framework import ISO_8601
from rest_framework import ISO_8601, STD_DURATION

DEFAULTS = {
# Base API policies
Expand Down Expand Up @@ -109,6 +109,8 @@
'TIME_FORMAT': ISO_8601,
'TIME_INPUT_FORMATS': [ISO_8601],

'DURATION_FORMAT': STD_DURATION,

# Encoding
'UNICODE_JSON': True,
'COMPACT_JSON': True,
Expand Down
25 changes: 24 additions & 1 deletion tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -1773,8 +1773,31 @@ class TestDurationField(FieldValues):
field = serializers.DurationField()


# Choice types...
class TestNoOutputFormatDurationField(FieldValues):
"""
Values for `TimeField` with a no output format.
Comment thread
auvipy marked this conversation as resolved.
Outdated
"""
valid_inputs = {}
invalid_inputs = {}
outputs = {
datetime.timedelta(1): datetime.timedelta(1)
}
field = serializers.DurationField(format=None)


class TestISOOutputFormatDurationField(FieldValues):
"""
Values for `TimeField` with a custom output format.
Comment thread
auvipy marked this conversation as resolved.
Outdated
"""
valid_inputs = {}
invalid_inputs = {}
Comment thread
peterthomassen marked this conversation as resolved.
Outdated
outputs = {
datetime.timedelta(days=3, hours=8, minutes=32, seconds=1, microseconds=123): 'P3DT08H32M01.000123S'
}
field = serializers.DurationField(format='iso-8601')


# Choice types...
class TestChoiceField(FieldValues):
"""
Valid and invalid values for `ChoiceField`.
Expand Down