如何在Django中按模板统计去重后的用户使用次数?
Django统计模板被不同用户使用次数的正确姿势
问题场景
模型结构简化为:
class Post(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) template_id = models.IntegerField(null=True) ...
要实现的是:统计每个模板被多少不同用户使用——同一用户哪怕多次用同一个模板,也只算1次。
之前尝试的常规统计代码会重复计数:
counts = Post.objects.all().values("template_id").order_by().annotate(count=Count("template_id"))
虽然用values("template_id", "user__id").distinct()能拿到唯一的用户-模板配对,但在此基础上再统计时,distinct的效果就没了,返回的count还是3(对应测试用例里的3条Post记录):
# 错误写法,结果不符合预期 Post.objects.all().values("template_id", "user__id").distinct().values("template_id").annotate(count=Count("template_id"))
测试用例:
user1 = baker.make("User") user2 = baker.make("User") # 填充数据:user1两次用模板1,user2一次用模板1 quest1 = baker.make("post.Post", user=user1, template_id=1) quest2 = baker.make("post.Post", user=user1, template_id=1) quest3 = baker.make("post.Post", user=user2, template_id=1)
预期结果是模板1被2位用户使用,但错误代码返回的是3。
正确解法
直接在Count里加distinct=True参数,统计去重后的用户ID:
counts = Post.objects.values("template_id").annotate( user_count=Count("user__id", distinct=True) ).order_by()
这样查询后,就能得到每个模板对应的唯一用户数,测试用例里会返回{'template_id': 1, 'user_count': 2},完全符合需求。
为啥之前的写法不行?
你之前的链式调用里,第二次values("template_id")会重置分组依据,相当于抛弃了之前distinct()得到的唯一配对,重新从原始Post表按template_id分组统计数量,自然会把同一用户的多次使用算进去。
如果偏好分步写,也可以先拿到去重的用户-模板对,再统计:
unique_user_template = Post.objects.values("template_id", "user__id").distinct() counts = unique_user_template.values("template_id").annotate(user_count=Count("user__id"))
不过这种写法不如直接用Count(distinct=True)高效,毕竟少了一次子查询。
内容的提问来源于stack exchange,提问作者kbunarjo
相关产品推荐
相关产品推荐

