【问题标题】:serialize a datetime as an integer timestamp将日期时间序列化为整数时间戳
【发布时间】:2021-02-27 21:43:12
【问题描述】:

我希望 django rest 在序列化时不要将我的 DateTime 模型字段转换为字符串日期表示。

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

我希望这样出来

1411880508

而不是

“2014-09-28T05:01:48.123”

【问题讨论】:

    标签: python django-rest-framework


    【解决方案1】:

    你会想写一个custom serializer field,像这样:

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

    为了支持您希望从 WritableField 继承并实现 from_native() 的写入操作。

    编辑 DRF 3.x 和 Python 3.8:

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

    如果你想要一个 JavaScript 样式的时间戳:

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

    【讨论】:

    • AFAIK 日期时间作为数字存储在数据库中。我希望得到这个数字,而不是遍历查询集中的所有记录。我担心这和我自己的方法都会对性能产生影响。
    • 如果你得到这个答案并使用 DRF 3.x,to_native 方法现在是to_representation
    【解决方案2】:
    REST_FRAMEWORK = {
        # if you want with milliseconds or
        'DATETIME_FORMAT': '%s.%f', 
        # only with seconds
        'DATETIME_FORMAT': '%s', 
    }
    

    REST 中的结果将是 string

    1. “1517863184.666435”

    2. “1517863249”

    如果您想要 API 中的浮点(或整数)值,则可以使用猴子补丁

    将文件monkey_patching.py 放入您的任何apps 并将其导入应用程序的__init__.py 文件中。即:

    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
    

    使用 Django 版本 2.0.10 和 Python 3.5.9 测试

    【讨论】:

    • 这个答案很棒!此外,它可以格式化为浮点数吗?
    • 我不这么认为,这也给我的 FilterSet 参数带来了一些问题
    • 但它不是 int
    • @MohammadTorkashvand 添加了 int 和 float 结果的信息
    • @YanQiDong 添加了 int 和 float 结果的信息
    【解决方案3】:

    我无法让 Tom 的示例正常工作,而且这些值似乎没有被修改。然而,它给了我一个起点,经过一番阅读,我找到了一种产生预期结果的方法:

    [方法 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 输出:

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

    [方法 2]

    Tom 的解释和前面提到的方法肯定更符合维护标准(因为结果实际上是整数类型)。

    然而,一个快速而肮脏的解决方案是指定 format parameter for the DateTimeField 并将其设置为以秒为单位显示值。

    请注意,这可能无法在 Windows 机器上正常工作! 并可能导致 ValueError: Invalid format string

    要试用它,只需在序列化器字段中包含“格式”关键字参数,如下所示:

    serializers.py

    class MySerializer(serializers.ModelSerializer):    
        timestamp = serializers.DateTimeField(format="%s")
    
        class Meta:
            model = myModel
            fields = ('id', 'ts')
    

    JSON 输出:

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

    另外你可以包括微秒:

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

    如果您想在自己的解释器中测试功能(以验证您的操作系统是否支持 %s 参数),只需复制以下行:

    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
    

    我觉得这种方法与 OPs 的问题有点不一致,因为结果实际上不是整数类型,而是整数/浮点数的字符串表示形式 - REST 会在值周围添加引号。

    【讨论】:

    • 这应该是公认的答案。 format="%s" 更干净!
    • 方法2的旁注,您可以在settings.py中设置全局默认格式,就像REST_FRAMEWORK = { 'DATETIME_FORMAT': '%s.%f'}一样
    【解决方案4】:

    虽然我更喜欢 Tom Christie 给出的答案,因为它更可靠。 为了潜在读者的利益,我决定发布我的解决方案

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

    【讨论】:

      【解决方案5】:

      全局配置:

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

      【讨论】:

        【解决方案6】:

        在 python 中,时间戳是 10 位数字。但是,在 Javascript 中,它是 13 位数字。

        因此,如果要在全局配置中返回Javascript格式的时间戳,只需在'%s'后面加上'000'即可:

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

        结果将如下所示:1567413479000

        【讨论】:

          【解决方案7】:

          如前所述,您可以通过以下方式为所有日期时间全局设置时间戳格式:

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

          但这不适用于常规日期,要使其适用于您还必须设置的日期:

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

          【讨论】:

            【解决方案8】:

            感谢@megajoe monkey patch solution。我是在 windows 上开发的,所以得到了无效的格式字符串,因为 windows 不支持任何 "%s" 格式 (http://msdn.microsoft.com/en-us/library/fe06s4ak.aspx)。

            所以我使用了像 @megajoe 这样的猴子补丁,并稍微调整了解决方案以返回 value.timestamp() 为 "%s.%f" 和 int(value.timestamp()) 为 "%s"。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-06-07
              • 1970-01-01
              • 2022-01-14
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多