Django-Group by 和 Count by unique 一起

问题描述

我有以下型号:

class Post(models.Model):
    title = models.CharField(max_length=30)

class PostView(models.Model):
    post = models.ForeignKey(Post,related_name='views',on_delete=models.CASCADE)
    user = models.ForeignKey(get_user_model(),related_name='my_views')
    created = models.DateTimeField(auto_Now_add=True)

我想获得帖子查看次数,按一天中的小时分组并且是唯一的。
例如,如果用户在上午 10 点看到帖子 20 次,则应仅计算一次。 我在几个小时内按视图(不是唯一视图)获得帖子,如下所示:

from django.db.models.functions import TruncHour
from django.db.models import Count

qs = PostView.objects.all().annotate(
        hour=TruncHour('created')
    ).values(
        'hour'
    ).annotate(
        c=Count('id')
    ).values('hour','c')

以上代码将所有视图计算为总视图。我想通过 user_idhourpost_id 一起获得独特的视图。 可以用 ORM 做到这一点吗?

解决方法

你可以这样做,

from django.db.models import Count

result = PostView.objects.values(
    "created__hour","post","user"
).annotate(count=Count("id"))

print(list(result))

# Result
# [{'created__hour': 17,'post': 1,'user': 1,'count': 4},{'created__hour': 17,'post': 2,'count': 3}]
,

简答 SQL 和 Django

select a.day_hour,count(*) from (select strftime('%Y-%m-%d %H',created) as day_hour,user_id,count(*)  from post_postview 
where post_id=1 group by strftime('%Y-%m-%d %H',created),user_id) 
a group by a.day_hour

姜戈回答

In [140]: rs = PostView.objects.filter(post_id=1).extra(
{'date_hour': u"strftime('%%Y-%%m-%%d %%H',created)"}).order_by('date_hour').values('user_id','date_hour').annotate(count=Count('user_id',distinct=True))

In [141]: rs
Out[141]: <QuerySet [{'date_hour': '2021-05-28 10','user_id': 2,'count': 1},{'date_hour': '2021-05-28 10','user_id': 3,{'date_hour': '2021-05-28 11','count': 1}]>

In [142]: rs.values('date_hour').distinct()
Out[142]: <QuerySet [{'date_hour': '2021-05-28 10'},{'date_hour': '2021-05-28 11'}]>

您需要分组两次。第一次在 date_houruser_id 上,第二次在 date_hour 上的现有结果集上。

长答案:

由于查询分为两个级别(日期级别和唯一用户),因此您需要两个查询。

在第一步中,您对 post_hour 创建的帖子进行分组。没有这个基本的聚合结果将显示错误的值。

db.sqlite3> select strftime('%Y-%m-%d %H',created) as 
day_hour,count(*)  from post_postview where 
post_id=1 group by strftime('%Y-%m-%d %H',user_id
+---------------+---------+----------+
| day_hour      | user_id | count(*) |
+---------------+---------+----------+
| 2021-05-28 10 | 2       | 1        |
| 2021-05-28 10 | 3       | 2        |
| 2021-05-28 11 | 2       | 3        |
| 2021-05-28 11 | 3       | 2        |
+---------------+---------+----------+

如您所见,对于相同的时间间隔(2021-05-28 10), 有 2 行。现在要计算这两行,需要额外的查询。

再次通过 day_hour 应用同一组,我们每小时得到结果。

select a.day_hour,count(*) from (select strftime('%Y-%m-%d 
%H',count(*)  from 
post_postview where post_id=1 group by strftime('%Y-%m-%d 
%H',user_id) a group by a.day_hour;

+---------------+----------+
| day_hour      | count(*) |
+---------------+----------+
| 2021-05-28 10 | 2        |
| 2021-05-28 11 | 2        |
+---------------+----------+

这里我使用了 SQLite 特定的 strftime,这是重要的部分。

相同的代码被移植到Django

In [145]: 
PostView.objects.filter(post_id=1).extra({'date_hour': 
u"strftime('%%Y-%%m-%%d %%H','date_hour').values('date_hour').distinct()
Out[145]: <QuerySet [{'date_hour': '2021-05-28 10'},{'date_hour': '2021-05-28 11'}]>

extra 方法让您注入 SQL 特定的函数,然后按照通用的 Django order_bydistinct 执行结果。 SQLite 不支持 distinct on。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...