基于日期查询Django ORM中的值

1 投票
3 回答
3726 浏览
提问于 2025-04-15 14:02

我有一堆对象,每个对象都有一个值和一个日期字段:

obj1 = Obj(date='2009-8-20', value=10)
obj2 = Obj(date='2009-8-21', value=15)
obj3 = Obj(date='2009-8-23', value=8)

我想要得到这样的结果:

[10, 15, 0, 8]

或者更好的是,得到一个截至某个时间点的总和:

[10, 25, 25, 33]

最好是直接从数据库获取这些数据,不过如果不行,我可以用一个循环很容易地计算总和。

我正在使用Django的ORM和Postgres数据库。

补充说明:

我想说的是,我的例子只涵盖了几天,但实际上,我有数百个对象,跨越了几十年……我想做的是创建一张折线图,显示我所有对象的总和是如何随着时间增长的(时间跨度非常长)。

3 个回答

0

如果你循环调用 Obj.objects.get 100 次,那你就会发起 100 次数据库查询。相反,使用 Obj.objects.filter 可以一次性返回结果,只不过这样会选择所有模型字段。正确的做法是使用 Obj.objects.values_list,这样可以通过一次查询,只选择你需要的 'values' 字段。

start_date = date(2009, 8, 20)
end_date = date(2009, 8, 23)

objects = Obj.objects.filter(date__range=(start_date,end_date))
# values_list and 'value' aren't related. 'value' should be whatever field you're querying
val_list = objects.values_list('value',flat=True)
# val_list = [10, 15, 8]

如果你想对 val_list 进行累加计算,可以这样做(我不确定这是不是最符合 Python 风格的方法)

for i in xrange(len(val_list)):
    if i > 0:
        val_list[i] = val_list[i] + val_list[i-1]

# val_list = [10,25,33]

编辑:如果你需要考虑缺失的日期,@Glenn Maynard 的回答其实挺不错的,虽然我更喜欢 dict() 的写法:

objects = Obj.objects.filter(date__range=(start_date,end_date)).values('date','value')
val_dict = dict((obj['date'],obj['value']) for obj in objects)
# I'm stealing datetimeRange from @Glenn Maynard
val_list = [val_dict.get(day, 0) for day in datetimeRange(start_date, end_date)]
# val_list = [10,15,0,8]
0
result_list = []
for day in range(20,24):    
    result = Obj.objects.get(date=datetime(2009, 08, day))
    if result:
        result_list.append(result.value)
    else:
        result_list.append(0)
return result_list

如果你在某一天有多个对象(Obj),你需要先检查一下这些对象的数量(len(obj)),然后逐个处理它们,特别是当数量超过1个的时候。

4

这个代码没有经过测试,因为要设置一个Django表来测试实在是太麻烦了:

from datetime import date, timedelta
# http://www.ianlewis.org/en/python-date-range-iterator
def datetimeRange(from_date, to_date=None):
    while to_date is None or from_date <= to_date:
        yield from_date
        from_date = from_date + timedelta(days = 1)

start = date(2009, 8, 20)
end = date(2009, 8, 23)
objects = Obj.objects.filter(date__gte=start)
objects = objects.filter(date__lte=end)

results = {}
for o in objects:
    results[o.date] = o.value

return [results.get(day, 0) for day in datetimeRange(start, end)]

这样做可以避免每天都要单独运行一个查询。

撰写回答