Update: Thanks to @maiksprenger for pointing out an alternative to convert to DateTime
.
I'm currently trying to deserialize JSON data to a Django model. This is quite straight forward for most numeric fields, but I have a DateTime
field which is stored as a timestamp in the original JSON.
Using the DateTimeField
At first, I thought I should read the timestamp as an integer
and convert it through post-processing. Then, after reading more of the DRF docs, it became evident I needed a custom field. The code is below:
from rest_framework import serializers
from datetime import datetime
class TimestampField(serializers.DateTimeField):
"""
Convert a django datetime to/from timestamp.
"""
def to_representation(self, value):
"""
Convert the field to its internal representation (aka timestamp)
:param value: the DateTime value
:return: a UTC timestamp integer
"""
result = super(TimestampField, self).to_representation(value)
return result.timestamp()
def to_internal_value(self, value):
"""
deserialize a timestamp to a DateTime value
:param value: the timestamp value
:return: a django DateTime value
"""
converted = datetime.fromtimestamp(float('%s' % value))
return super(TimestampField, self).to_representation(converted)
The idea is that to_representation
converts a DateTime
field into a timestamp value for serialisation and to_internal_value
does exactly the opposite:
-
to_representation
: just gets thetimestamp()
value out of theDateTime
field -
to_internal_value
: converts the timestamp value to aDateTime
object via:datetime.fromtimestamp(float('%s' % value))
Using FloatField serializer
Maik Sprenger pointed out a faster way, by serialising to/from a FloatField:
from rest_framework import serializers
from datetime import datetime
class UnixTimestampField(serializers.FloatField):
"""
Stores Django datetimes as a Unix timestamp.
Code by Maik Sprenger
https://gist.github.com/maiksprenger/1b67662c489d7b710242190de9a992ff
"""
def to_internal_value(self, value):
"""
Convert a float of a Unix timestamp to an aware datetime
"""
# Convert given value to float, or fail
timestamp = super().to_internal_value(value)
# Convert to aware datetime
# If a timezone is given, fromtimestamp will always create a datetime for the
# same point in time. The only difference is the associated timezone.
# But that is slightly surprising, so to make it least surprising, we're
# returning an aware datetime based on UTC.
return datetime.fromtimestamp(timestamp, tz=timezone.utc)
def to_representation(self, value):
"""
Convert a datetime to a float of an Unix timestamp
Any timezone information is handled correctly by timestamp()
https://docs.python.org/3/library/datetime.html#datetime.datetime.timestamp
"""
return value.timestamp()
This is all!
HTH,
Member discussion: