【问题标题】:query for values based on date w/ Django ORM使用 Django ORM 根据日期查询值
【发布时间】:2009-09-03 03:20:05
【问题描述】:

我有一堆具有值和日期字段的对象:

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]

我最好直接从数据库中获取这些数据,否则我可以使用 forloop 轻松完成总计。

我正在使用 Django 的 ORM 和 Postgres

编辑:

请注意,我的示例仅涵盖了几天,但实际上,我有数百个对象涵盖了几十年...我要做的是创建一个折线图,显示所有对象的总和我的对象随着时间的推移而增长(很长一段时间)

【问题讨论】:

  • 数据库?什么数据库?直到我们走到最后并在标签中看到您正在使用 Django(因此可能是它的内置 ORM ——如果您使用的是不同的,您应该指定!),这个问题非常令人困惑;将来,请将该信息放在主题中或以其他方式放在前面。我在这里做了适当的编辑。
  • 哎呀,对不起。是的,我正在使用 django ORM,我也在使用 Postgres
  • 我们可以假设每个日期只有一个或零个对象吗?

标签: python django django-orm


【解决方案1】:

这个没有测试,因为设置一个 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)]

这样可以避免每天运行单独的查询。

【讨论】:

  • Django 查询集是惰性求值的,所以实际上你每天都会运行一个单独的查询。
  • 除了这里的查询集只评估一次,在for循环的开始,所以没问题。
  • 每次调用 o.value 时,您都会访问数据库,因此每天都会访问数据库。
  • 不,当objects.__iter__()被调用时,ORM会命中数据库并为所有满足过滤条件的Obj对象选择所有字段(日期,值等)。
  • 你很困惑,大卫。它不会针对每个 o.value 访问数据库。它运行单个查询,批量返回结果。
【解决方案2】:
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 个。

【讨论】:

    【解决方案3】:

    如果您循环遍历 Obj.objects.get 100 次,您将执行 100 个 SQL 查询。 Obj.objects.filter 将在一个 SQL 查询中返回结果,但您也可以选择所有模型字段。执行此操作的正确方法是使用 Obj.objects.values_list,它将通过单个查询执行此操作,并且仅选择“值”字段。

    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的运行聚合,你可以这样做(不确定这是最pythonic的方式)

    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]
    

    【讨论】:

    • 此答案无法为数据库中不存在的天数提供值。
    • -1 对我的解决方案做出虚假声明,然后给出与我完全相同的解决方案(但方式不太清楚)。
    • 当我写这篇文章时,我不清楚数据库是否会丢失日期。没错,如果数据库缺少日期,那么您的解决方案就可以了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-26
    • 1970-01-01
    • 2020-04-17
    • 1970-01-01
    • 1970-01-01
    • 2012-02-08
    • 1970-01-01
    相关资源
    最近更新 更多