Django按组分组并计数

5

我正在尝试对我的模型进行分组,它看起来像这样:

class Restaurant(models.Model):
    pass

class Order(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    restaurant = models.ForeignKey('Restaurant')

现在我想知道每天创建了多少订单。这意味着我需要从DateTime字段获取日期,然后进行计数。
如果相关的话,我是通过以下方式获取“Order”查询集的:
restaurant = Restaurant.objects.get(id=request.data['restaurant_id'])
orders = restaurant.order_set.filter(created__lte=some_date)

现在,我该如何从orders中获取我想要的内容呢?我尝试了以下方法:
orders.values('created').annotate(Count('created'))

使用 TruncDate 等相关功能。

我在使用 Python 2 和 Django 1.11。

编辑 为了更好地表达我的意图。我想要在纯 Python 中实现这样的东西:

orders_by_date = {}
for order in orders:
    if orders_by_date.get(datetime.date(orders[0].created.year, orders[0].created.month, orders[0].created.day):
        orders_by_date[datetime.date(orders[0].created.year, orders[0].created.month, orders[0].created.day)] += 1
    else:
        orders_by_date[datetime.date(orders[0].created.year, orders[0].created.month, orders[0].created.day)] = 1

编辑2 我成功地显示了单个日期的计数:

orders.filter(created__date=datetime.date(2018, 6, 8)).aggregate(Count('id'))

给了我{'id__count': 3}。现在最好按所有日期分组,而不仅仅是这一个。


当您说按所有日期分组时,是指获取每个日期的id__count吗? - 17slim
多多少少是的。确切地说:如果我在2018年1月1日12:00创建了1个订单,第二个订单在2018年1月1日13:00创建,第三个订单在2018年1月2日12:00创建,我希望输出类似于{date(2018, 1, 1): 2, date(2018, 1, 2): 1}。如果是 {'id__count': 2, 'created_date': date(2018, 1, 1), (...) 我也可以接受。 - gonczor
好的,我已经编辑了我的回答。 - 17slim
2个回答

2
你应该可以执行 orders.filter(created__date=datetime.date(2018, 6, 8)).values('id__count'),这将给你一个字典列表:[{'id__count': 3},{'id__count': 1},...]
以下是文档
编辑: 尝试使用annotate然后获取值:
orders_with_counts = orders.annotate(Count('id'))
dates_and_counts = orders_with_counts.values_list('created__date','id__count')
dct = dict(dates_and_counts)

dates_and_counts 是一个元组列表:[(date1,count1),(date2,count2)...]dct 是将其转换为字典后的结果。


它给了我{FieldError}无法将关键字'count'解析为字段。不允许在'id'上连接。但仍然没有解决分组问题。如果可能的话,我想在数据库中完成它,而不是在Python中。 - gonczor
抱歉,我误解了那部分。您想通过日期分组获得什么结果? - 17slim
正如我所说,我想要日期计数对。因此,如果我在2018年1月1日12:00创建了1个订单,第二个订单在2018年1月1日13:00创建,第三个订单在2018年1月2日12:00创建,我期望的输出类似于{date(2018, 1, 1): 2, date(2018, 1, 2): 1} - gonczor

2
您可以使用以下方式在一次查询中获取数据:
from django.db.models import DateField, Sum
from django.db.models.functions import Cast

query = Order.objects.filter(
    restaurant=some_restaurant
).annotate(
    create_date=Cast('created', DateField())
).values('create_date').annotate(
    id_count=Count('id')
).order_by('create_date')

这将返回一个类似于字典的QuerySet集合,例如:
<QuerySet [{'create_date': datetime.date(2017, 1, 31), 'id_count': 14}, 
           {'create_date': datetime.date(2017, 2, 2), 'id_count': 25}, 
           {'create_date': datetime.date(2017, 2, 9), 'id_count': 13},
           {'create_date': datetime.date(2017, 2, 10), 'id_count': 2}, 
           {'create_date': datetime.date(2017, 2, 16), 'id_count': 17},
           {'create_date': datetime.date(2017, 2, 17), 'id_count': 89}, 
           {'create_date': datetime.date(2017, 2, 20), 'id_count': 20},
           {'create_date': datetime.date(2017, 2, 23), 'id_count': 18}, 
           {'create_date': datetime.date(2017, 2, 24), 'id_count': 20},
           {'create_date': datetime.date(2017, 2, 28), 'id_count': 20}, 
           {'create_date': datetime.date(2017, 3, 1), 'id_count': 3},
           {'create_date': datetime.date(2017, 3, 3), 'id_count': 9}, 
           {'create_date': datetime.date(2017, 3, 7), 'id_count': 9},
           {'create_date': datetime.date(2017, 3, 9), 'id_count': 1}, 
           {'create_date': datetime.date(2017, 3, 10), 'id_count': 7}, 
           {'create_date': datetime.date(2017, 3, 14), 'id_count': 2}, 
           {'create_date': datetime.date(2017, 3, 15), 'id_count': 7},
           {'create_date': datetime.date(2017, 3, 17), 'id_count': 9}, 
           {'create_date': datetime.date(2017, 3, 22), 'id_count': 2},
           {'create_date': datetime.date(2017, 3, 24), 'id_count': 8},
           '...
           (remaining elements truncated)...']>

在幕后,它将生成类似以下的查询语句:
SELECT CAST(`order`.`created` AS date) AS `create_date`,
       COUNT(`order`.`id`) AS `id_count`
FROM `order`
WHERE `order`.`restaurant_id` = 123
GROUP BY CAST(`order`.`created` AS date)
ORDER BY `create_date` ASC

(这里的123是一个餐厅id的样例)。

因此,你可以遍历结果并构建一个JSON对象等。

我们可以通过使用字典推导式来将其转换为将datetime.date对象映射到计数的字典:

result = { t['create_date']: t['id_count'] for t in query }

请注意,没有任何Order的日期将不会成为查询集的一部分(也不会成为result字典的一部分)。这是合乎逻辑的,因为我们把Order表作为“根”,如果没有行,则不会有输出。

这解决了我的问题。如果有人对创建的查询感兴趣,我在这里粘贴它:In [57]: print(x.query) SELECT "papu_order"."created"::date AS "create_date", COUNT("papu_order"."id") AS "id_count" FROM "papu_order" WHERE "papu_order"."restaurant_id" = 1 GROUP BY "papu_order"."created"::date ORDER BY "create_date" ASC - gonczor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接