mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-10-24 04:31:08 +03:00
Whilst this commit adds *encoding* of timedeltas to a string of a floating
point value of the seconds, you must add your own serializer field for
whatever timedelta model field you are using. This is because Django doesn't
support any kind of timedelta field out-of-the-box, so you have to either
implement your own or use django-timedelta.
If this is the case and you want to serialise timedelta input, you will have
to implement your own special field to use for the timedelta, which is not
included in core as it is based on a 3rd party library. Here is an example:
import datetime
import timedelta
from django import forms
from django.core import validators
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from rest_framework.fields import WritableField
class TimedeltaField(WritableField):
type_name = 'TimedeltaField'
form_field_class = forms.FloatField
default_error_messages = {
'invalid': _("'%s' value must be in seconds."),
}
def from_native(self, value):
if value in validators.EMPTY_VALUES:
return None
try:
return datetime.timedelta(seconds=float(value))
except (TypeError, ValueError):
msg = self.error_messages['invalid'] % value
raise ValidationError(msg)
Which is based on the FloatField. This field can then be used in
your serializer like this:
from yourapp.fields import TimedeltaField
class YourSerializer(serializers.ModelSerializer):
duration = TimedeltaField()
94 lines
3.5 KiB
Python
94 lines
3.5 KiB
Python
"""
|
|
Helper classes for parsers.
|
|
"""
|
|
import datetime
|
|
import decimal
|
|
import types
|
|
import json
|
|
from django.utils.datastructures import SortedDict
|
|
from rest_framework.compat import timezone
|
|
from rest_framework.serializers import DictWithMetadata, SortedDictWithMetadata
|
|
|
|
|
|
class JSONEncoder(json.JSONEncoder):
|
|
"""
|
|
JSONEncoder subclass that knows how to encode date/time/timedelta,
|
|
decimal types, and generators.
|
|
"""
|
|
def default(self, o):
|
|
# For Date Time string spec, see ECMA 262
|
|
# http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
|
|
if isinstance(o, datetime.datetime):
|
|
r = o.isoformat()
|
|
if o.microsecond:
|
|
r = r[:23] + r[26:]
|
|
if r.endswith('+00:00'):
|
|
r = r[:-6] + 'Z'
|
|
return r
|
|
elif isinstance(o, datetime.date):
|
|
return o.isoformat()
|
|
elif isinstance(o, datetime.time):
|
|
if timezone and timezone.is_aware(o):
|
|
raise ValueError("JSON can't represent timezone-aware times.")
|
|
r = o.isoformat()
|
|
if o.microsecond:
|
|
r = r[:12]
|
|
return r
|
|
elif isinstance(o, datetime.timedelta):
|
|
return str(o.total_seconds())
|
|
elif isinstance(o, decimal.Decimal):
|
|
return str(o)
|
|
elif hasattr(o, '__iter__'):
|
|
return [i for i in o]
|
|
return super(JSONEncoder, self).default(o)
|
|
|
|
|
|
try:
|
|
import yaml
|
|
except ImportError:
|
|
SafeDumper = None
|
|
else:
|
|
# Adapted from http://pyyaml.org/attachment/ticket/161/use_ordered_dict.py
|
|
class SafeDumper(yaml.SafeDumper):
|
|
"""
|
|
Handles decimals as strings.
|
|
Handles SortedDicts as usual dicts, but preserves field order, rather
|
|
than the usual behaviour of sorting the keys.
|
|
"""
|
|
def represent_decimal(self, data):
|
|
return self.represent_scalar('tag:yaml.org,2002:str', str(data))
|
|
|
|
def represent_mapping(self, tag, mapping, flow_style=None):
|
|
value = []
|
|
node = yaml.MappingNode(tag, value, flow_style=flow_style)
|
|
if self.alias_key is not None:
|
|
self.represented_objects[self.alias_key] = node
|
|
best_style = True
|
|
if hasattr(mapping, 'items'):
|
|
mapping = list(mapping.items())
|
|
if not isinstance(mapping, SortedDict):
|
|
mapping.sort()
|
|
for item_key, item_value in mapping:
|
|
node_key = self.represent_data(item_key)
|
|
node_value = self.represent_data(item_value)
|
|
if not (isinstance(node_key, yaml.ScalarNode) and not node_key.style):
|
|
best_style = False
|
|
if not (isinstance(node_value, yaml.ScalarNode) and not node_value.style):
|
|
best_style = False
|
|
value.append((node_key, node_value))
|
|
if flow_style is None:
|
|
if self.default_flow_style is not None:
|
|
node.flow_style = self.default_flow_style
|
|
else:
|
|
node.flow_style = best_style
|
|
return node
|
|
|
|
SafeDumper.add_representer(SortedDict,
|
|
yaml.representer.SafeRepresenter.represent_dict)
|
|
SafeDumper.add_representer(DictWithMetadata,
|
|
yaml.representer.SafeRepresenter.represent_dict)
|
|
SafeDumper.add_representer(SortedDictWithMetadata,
|
|
yaml.representer.SafeRepresenter.represent_dict)
|
|
SafeDumper.add_representer(types.GeneratorType,
|
|
yaml.representer.SafeRepresenter.represent_list)
|