serialize a datetime as an integer timestamp

佐手、 提交于 2021-02-04 11:23:26

问题


I would like for django rest to not convert my DateTime model field into a string date represtation when serializing it.

response_date = serializers.DateTimeField(source="updated_at")

I would like this to come out as

1411880508

and not

"2014-09-28T05:01:48.123"


回答1:


You'll want to write a custom serializer field, like so:

class TimestampField(serializers.Field):
    def to_native(self, value):
        epoch = datetime.datetime(1970,1,1)
        return int((value - epoch).total_seconds())

To support write operations you'd want to inherit from WritableField and also implement from_native().

EDIT for DRF 3.x & Python 3.8:

class TimestampField(serializers.Field):
    def to_representation(self, value):
        return value.timestamp()

If you want a JavaScript style timestamp:

class JsTimestampField(serializers.Field):
    def to_representation(self, value):
        return round(value.timestamp()*1000)



回答2:


REST_FRAMEWORK = {
    # if you want with milliseconds or
    'DATETIME_FORMAT': '%s.%f', 
    # only with seconds
    'DATETIME_FORMAT': '%s', 
}

Result in REST will be string

  1. "1517863184.666435"

  2. "1517863249"

If you want float(or integer) value in API, than you can use monkey patching.

Put the file monkey_patching.py in any of your apps and import it in app's __init__.py file. ie:

app/monkey_patching.py

#app/monkey_patching.py
import six
from rest_framework import ISO_8601
from rest_framework.fields import DateTimeField
from rest_framework.settings import api_settings


def to_representation_ext(self, value):
    if not value:
        return None

    output_format = getattr(self, 'format', api_settings.DATETIME_FORMAT)

    if output_format is None or isinstance(value, six.string_types):
        return value

    if output_format.lower() == ISO_8601:
        value = self.enforce_timezone(value)
        value = value.isoformat()
        if value.endswith('+00:00'):
            value = value[:-6] + 'Z'
        return value
    
    # FOR INTEGER RESULT 'DATETIME_FORMAT': '%s',
    # return int(value.strftime(output_format))

    # FOR FLOAT RESULT 'DATETIME_FORMAT': '%s.%f',
    return float(value.strftime(output_format))


DateTimeField.to_representation = to_representation_ext

app/init.py

#app/__init__.py
import app.monkey_patching

Tested with Django version 2.0.10 and Python 3.5.9




回答3:


I was not able to get Tom's example to work and it seemed the values were not being modified. However it gave me a starting point and after some reading I found a way to produce the desired result:

[METHOD 1]

serializers.py

import time

class TimestampField(serializers.Field):
    def to_representation(self, value):
        return int(time.mktime(value.timetuple()))

class MySerializer(serializers.ModelSerializer):
    ts = TimestampField(source="my_fieldname") #Source must be a models.DateTimeField

    class Meta:
        model = myModel
        fields = ('id', 'ts')

JSON output:

[{
    "id": 1,
    "ts": 1475894303
},
{
    "id": 2,
    "ts": 1475833070 
}]

[METHOD 2]

Tom's explanation and the previous mentioned method are definitely more on track with maintaining standards (as the results are actually of type integer).

However a quick and dirty solution is to specify the format parameter for the DateTimeField and set it to show the value in seconds.

Note this probably won't work correctly on Windows machines! And may result in a ValueError: Invalid format string

To try it out just include the "format" keyword parameter in your serializer field like so:

serializers.py

class MySerializer(serializers.ModelSerializer):    
    timestamp = serializers.DateTimeField(format="%s")

    class Meta:
        model = myModel
        fields = ('id', 'ts')

JSON output:

[{
    "id": 1,
    "ts": "1475890361"
},
{
    "id": 2,
    "ts": "1475833070"
}]

Additionally you may include microseconds:

timestamp = serializers.DateTimeField(format="%s.%f")

If you want to test the functionality in your own interpreter (to verify your OS supports the %s parameter) just copy over these lines:

import datetime
print datetime.datetime.now().strftime('%s') #datetime formatted as seconds for REST

import time  #This is just for confirmation
print time.mktime(datetime.datetime.now().timetuple()) #time object result as float

I feel this method is a little inconsistent with the OPs question because the result is not actually of type integer, instead it is a string representation of an integer/float - and REST will surly add quotes around the value.




回答4:


Although I prefer the answer given by Tom Christie as it is more robust. I decided to post my solution for the benefit of the potential readers

response_date = serializers.SerializerMethodField('get_timestamp')
def get_timestamp(self, obj):
    #times 1000 for javascript.
    return time.mktime(obj.updated_at.timetuple()) * 1000



回答5:


Global Configuration:

REST_FRAMEWORK = {
    'DATETIME_FORMAT': '%s.%f',
}



回答6:


In python, the timestamp is 10 digit. However, in Javascript, it is 13 digit.

Therefore, if you want to return Javascript format timestamp in global configure, just add '000' after '%s':

JS_TIMESTAMP = '%s000'

REST_FRAMEWORK = {
    'DATETIME_FORMAT': JS_TIMESTAMP,
    'DATE_FORMAT': JS_TIMESTAMP
}

The result will looks like this: 1567413479000




回答7:


As mentioned before you can set timestamp format for all datetimes globally by:

REST_FRAMEWORK = {
    'DATETIME_FORMAT': '%s',
}

But this doesnt work for regular dates, to make it work for dates you also have to set:

REST_FRAMEWORK = {
    'DATE_FORMAT': '%s',
}


来源:https://stackoverflow.com/questions/26083583/serialize-a-datetime-as-an-integer-timestamp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!