我有一对简单的模型用于处理时间序列:
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
数据类型。)
感谢。
感谢您的评论!所以当然我被烧了因为我已经过度简化了上面的例子。在我的实际代码中,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
进行比较。
答案 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年是年份的最小值。
我更倾向于使用此方法转换为文本,但这是您的选择。