Django不支持约会

时间:2013-07-22 20:50:49

标签: django django-templates django-template-filters

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

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" %}替换突出显示的行,一切正常。但是,如果我改为写{% 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可以与Float数据干净地比较,但不能与DateTime数据进行比较吗?如果是这样,将所有时间戳强制转换为latest_timestamp_text格式(YYYYMMDD HHMMSS转换为None)的0方法是否足以作为执行以下操作的关键字?模板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所指出的,这不足以使“安全”日期时间tz意识到。所以我只是放弃了,并采用了字符串化的想法。工作良好。与预期方式中的None进行比较。

1 个答案:

答案 0 :(得分:1)

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

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

列表也是空白的。

因此解决方案是将datetime对象转换为文本并使用它,同时返回''而不是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会产生带有时区信息的datetime对象。因此,如果没有该信息,它当然无法与普通的日期时间对象进行比较:

>>> 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年是年份的最小值。

我更倾向于使用此方法转换为文本,但这是您的选择。