如何创建一个 Django 自定义字段来存储 MYSQL DATETIME(6) 并在 Django/MySQL 中启用小数秒(毫秒或微秒)?

Posted

技术标签:

【中文标题】如何创建一个 Django 自定义字段来存储 MYSQL DATETIME(6) 并在 Django/MySQL 中启用小数秒(毫秒或微秒)?【英文标题】:How to create a Django custom Field to store MYSQL DATETIME(6) and enable fractional seconds (milliseconds and or microseconds) in Django/MySQL? 【发布时间】:2013-07-24 15:59:38 【问题描述】:

mysql 5.6.4 及更高版本扩展了对 TIME、DATETIME 和 TIMESTAMP 值的小数秒支持,精度高达微秒(6 位):http://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html

Django 1.5 及更高版本支持小数秒作为输入格式:https://docs.djangoproject.com/en/1.5/ref/settings/#datetime-input-formats

但 DATETIME(6) 字段尚未在 Django 中实现。 https://code.djangoproject.com/ticket/19716

我决定编写一个自定义的 DateTimeFractionField。它是具有 DATETIME([1-6]) db_type 的标准 DateTimeField。 'precicion' 是设置毫秒、微秒或任何其他小数精度。

class DateTimeFractionField(models.DateTimeField):
    description = "Datetimefield with fraction second."

    def __init__(self, precision, *args, **kwargs):
        self.precision = precision
        super(DateTimeFractionField, self).__init__(*args, **kwargs)

    def db_type(self, connection):
        return 'DATETIME(%s)' % self.precision


class MyModel(models.Model):
    dt_micros  = DateTimeFractionField(6)
    dt_millis = DateTimeFractionField(3)
    dt = models.DateTimeField()

    def __unicode__(self):
        return "%s - %s" %(self.dt_micros, self.dt_millis)

mysql 后端负责将毫秒替换为 0。Django 文档建议编写我自己的后端。 https://docs.djangoproject.com/en/1.5/ref/settings/#engine

我被黑了:

$ cd /path/to/site-packages/django/db/backends/
$ cp -r mysql mysql564

并修改mysql564/base.py:

42:45 行

from django.db.backends.mysql564.client import DatabaseClient
from django.db.backends.mysql564.creation import DatabaseCreation
from django.db.backends.mysql564.introspection import DatabaseIntrospection
from django.db.backends.mysql564.validation import DatabaseValidation

第 167 行

supports_microsecond_precision = True

第 214 行

compiler_module = "django.db.backends.mysql564.compiler"

第 357 行

return six.text_type(value) #value.replace(microsecond=0)

第 368 行

return six.text_type(value) #value.replace(microsecond=0)

第 373 行

return [first, second] #return [first.replace(microsecond=0), second.replace(microsecond=0)]

然后我在 settings.py 中激活了我的新后端:

'ENGINE': 'django.db.backends.mysql564',

当我在管理员中添加并保存我的模型时,值会保存到 de db! :)

但它们不会被返回(无 - 无和空表单字段)。 :(

我在这里缺少什么?

    为什么不返回 DateTimeFractionField 值? 是否有更好(更简单)的方法来实现支持分数的日期时间字段?

我知道还有其他 db 的支持分数。但我喜欢使用 MySQL 并获得更接近固定的票证。

更新:

这不是(仅)表单,从 de db 获取 datetime 对象失败。

In [1]: from spacetime.models import MyModel

In [2]: from django.shortcuts import get_object_or_404

In [3]: get_object_or_404(MyModel, pk=1).dt
Out[3]: datetime.datetime(2013, 7, 25, 0, 22, 23)

In [4]: get_object_or_404(MyModel, pk=1).dt_millis

In [5]: get_object_or_404(MyModel, pk=1).dt_millis.__class__
Out[5]: NoneType #This should be datetime.datetime

【问题讨论】:

也许只有我一个人,但看到您实际要求/尝试做的事情有点令人困惑。 我尝试创建一个支持小数秒的 DateTimeField。我的自定义 DateTimeField 是 DateTimeFractionField。它成功地将带有分数的日期时间对象保存到数据库,但从数据库获取值到 Django 不起作用。总体目标是获得一个有效的 DateTimeFractionField 字段。目前的问题:为什么没有返回 DateTimeFractionField 值? 据我了解,Django 不支持开箱即用,因为 SQL 并没有真正的规范。您可以使用 CharField,或者使用特定于数据库的代码编写自己的字段。 感谢您的评论。 This ticket 显示可以做什么。 CharField 并不是一个真正的选择。无法查询日期时间字段等字符字段。我的问题提供了一个自定义字段和一个自定义数据库后端来处理微秒(它应该可以工作)。我猜数据库接口(MySQLdb)出了问题。 【参考方案1】:

我已经成功地尝试过了,但我的方法完全不同: 我将信息拆分为 DateTimeField (UTC) 和 IntegerField (microseconds = uS):

from datetime                   import datetime, timedelta
import pytz

class MyModel (models.Model):
    # My model stuff and other fields here
    _UTC = models.DateTimeField ()
    _uS  = models.IntegerField()

然后我在类中设置一个属性以返回具有微秒和区域信息感知的 UTC(因为我的数据库不支持 zoneinfo)

    @property
    def UTC (self): 
        utc = self._UTC
        us  = self._uS
        # add microseconds
        utc += timedelta(microseconds=us)
        return utc.replace(tzinfo=pytz.utc)
    @UTC.setter
    def UTC (self,utc):
        self._UTC = utc-timedelta(microseconds=utc.microsecond) # without microseconds
        self._uS = utc.microsecond

【讨论】:

不错。我会试试这个。 Django 1.8 将支持分数:docs.djangoproject.com/en/dev/releases/1.8/#database-backends 请注意@property 字段不适用于过滤查询(MyModel.objects.filter(UTC...)。为此,需要分别按 _UTC 和/或 _uS 过滤

以上是关于如何创建一个 Django 自定义字段来存储 MYSQL DATETIME(6) 并在 Django/MySQL 中启用小数秒(毫秒或微秒)?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Django 管理员中为模型字段使用自定义表单字段?

Django 自定义表单字段错误地呈现值

自定义 Django 模型字段中的“对象没有属性”

你如何在 django 中加载自定义字段

使用自定义字段扩展 Django 的 ManyToManyField

使用自定义模板时 Django 表单字段的初始值