Django dictsort 不适用于日期

Posted

技术标签:

【中文标题】Django dictsort 不适用于日期【英文标题】:Django dictsort not working with date 【发布时间】:2013-07-21 18:06:10 【问题描述】:

我有一对用于处理时间序列的简单模型:

class Ticker(models.Model):
    name = CharField(max_length=100)

     def latest_datapoint(self):
         return self.datapoint_set.order_by('-timestamp')[0]

     def latest_value(self):
         return self.latest_data_point().value

     def latest_timestamp(self)
         return self.latest_data_point().timestamp

class DataPoint(models.Model):
     ticker = ForeignKeyField(Ticker)
     timestamp = DateTimeField()
     value = FloatField()

然后我使用基于通用类的视图来制作股票代码列表:

class TickerListView(ListView):
    model = Ticker
    template_name = 'ticker-list.html'

最后,我实现了模板:

<table>
    <thead>
        <tr>
            <th>Name</th>
            <th>Latest Value</th>
            <th>Latest Timestamp</th>
        </tr>
    </thead>
    <tbody>
        % for object in object_list %  <----***Let's talk about this***
            <tr>
                <td> object.name </td>
                <td> object.latest_value</td>
                <td> object.latest_timestamp</td>
            </tr>
        % endfor % 
    </tbody>
</table>

正如所写,这工作得很好。但我想使用 dictsort在模板中订购我的结果。如果我用% for object in object_list|dictsort:"latest_value" % 替换突出显示的行,一切正常。但是,如果我改为 write% for object in object_list|dictsort:"latest_timestamp" %,所有内容都是空白的,就好像我指定了一个无效的键来排序一样。知道这里发生了什么吗?

(假设:dictsort在排序时不知道如何处理datetime.datetime数据类型。)

谢谢。

添加以回应 Hieu Nguyen:

感谢您的评论!所以我当然会被烧毁,因为我把上面的例子过于简单化了。在我的实际代码中,Ticker 的方法定义如下:

     def latest_datapoint(self):
         if self.datapoint_set:
             return self.datapoint_set.order_by('-timestamp')[0]
         else:
             return None

     def latest_value(self):
         if self.latest_datapoint():
             return self.latest_data_point().value
         else:
             return None

     def latest_timestamp(self)
         if self.latest_datapoint():             
             return self.latest_data_point().timestamp
         else:
             return None

当我尝试对 latest_value 进行 dictsort 时,一切正常,即使有几个 Tickers 有 latest_value() = None。但是当我对latest_timestamp 进行排序时,其中一些是None 时,Django 似乎完全跳过了这个表。这里的教训是None 可以清楚地与浮点数据进行比较,但不能与日期时间数据进行比较?如果是这样,将所有时间戳强制转换为YYYYMMDD HHMMSS 格式(将None 转换为0)的latest_timestamp_text 方法是否足以作为执行模板内dictsorts 的键?

再次编辑:

这似乎仍然无法解决。我已经实现了一个新方法:

def latest_timestamp_safe(self):
    if self.latest_timestamp() is None:
        return datetime.now().replace(year=1975)
    else:
        return self.latest_timestamp()

我已经确认这是有效的,并且我表中的所有行在我为 latest_timestamp_safe 创建的列中都显示了一个不错的 tz 感知日期时间。但是我仍然不能对这个键进行排序。有什么想法吗?

上次编辑:

以上不起作用。正如 Hieu 指出的那样,这不足以让“安全”的 datetime tz 意识到这一点。所以我放弃了这个想法,并采用了字符串化的想法。工作正常。以预期的方式与None 进行比较。

【问题讨论】:

这适用于我的测试,所以可以肯定 datetime 不是问题,你确定 latest_timestamp 对整个列表都显示正常吗? 感谢您抽出宝贵时间查看我的问题!我在上面的例子中过于简化了。我在我的编辑中给出了更彻底和准确的描述。请让我知道您的想法。 【参考方案1】:

你的假设是正确的。似乎dictsort 无论如何都无法将None 值与datetime 对象进行比较。即使您返回一个非常小的日期时间对象,例如:

datetime.datetime(1900, 1, 1, 1, 1)

列表也将是空白的。

因此解决方案是将日期时间对象转换为文本并使用它,同时返回'' 而不是None,因此在排序时将None 转换为其他东西不需要时间:

def latest_timestamp_text(self)
    if self.latest_datapoint():             
        return self.latest_data_point().timestamp.strftime('%Y-%m-%d %H:%M:%S')
    else:
        return ''

希望对你有帮助。

编辑

我不知道 Django 的 DateTimeField 会生成带有时区信息的日期时间对象。所以当然它不能与没有该信息的普通日期时间对象进行比较:

>>> dt1 = datetime.datetime(2006, 11, 21, 16, 30, tzinfo=gmt2)
>>> dt2 = datetime.datetime(2006, 11, 22, 16, 30)
>>> dt1 > dt2
TypeError: can't compare offset-naive and offset-aware datetimes

因此,如果您仍想使用 datetime 对象而不转换为文本,则必须创建一个时区信息类,如下所示:

from datetime import datetime, timedelta, tzinfo

class GMT2(tzinfo):
    def utcoffset(self, dt):
        return timedelta(hours=2) + self.dst(dt)

    def dst(self, dt):
        d = datetime(dt.year, 4, 1)
        self.dston = d - timedelta(days=d.weekday() + 1)
        d = datetime(dt.year, 11, 1)
        self.dstoff = d - timedelta(days=d.weekday() + 1)
        if self.dston <= dt.replace(tzinfo=None) < self.dstoff:
            return timedelta(hours=1)
        else:
            return timedelta(0)

    def tzname(self, dt):
        return "GMT +2"

然后像这样改变你的功能:

def latest_timestamp(self):
    gmt2 = GMT2()
    if self.latest_datapoint():             
        return self.latest_data_point().timestamp
    else:
        return datetime(1970, 1, 1, 1, 1, tzinfo=gmt2)

注意:1970 是年份的最小值。

我更愿意通过这种方法转换为文本,但这是您的选择。

【讨论】:

我很震惊datetime.datetime(1900,1,1,1,1) 仍然不起作用。知道为什么会这样吗?这可能是时区问题吗? DateTimeField 值都会有一些时区数据,但默认情况下会是 1900 年吗? 不,datetime.now() 不会创建 tz 感知日期时间对象。如果您想创建这样的对象,请使用与GMT2 类相同的方式,即gmt2 = GMT2() 然后return datetime.now(gmt2).replace(year=1975)。你不能只解决这个问题;) 哈。好的。回到绘图板。 非常棒的答案,Hieu。我把日期串起来了,效果很好。 None 和字符串以预期的方式进行比较。

以上是关于Django dictsort 不适用于日期的主要内容,如果未能解决你的问题,请参考以下文章

JS 代码不适用于从 Django 模型中检索到的所有数据

Django内置模版过滤器

年份(日期)= '2010' 的 Django 查询集

为啥css文件不适用于html文件 - Django

Django-filters 不适用于 Viewset

django 多数据库路由不适用于多个模式