Django QuerySet如何按活动日期过滤聚合计算的用户总积分
问题解决方法
报错原因
你遇到的ValueError: too many values to unpack (expected 2)错误是filter的语法错误导致的:Django的filter方法接收的是键值对形式的关键字参数或者Q对象,你直接把完整的过滤条件作为字符串传入,Django无法解析该字符串为键值对,就触发了解包错误。
正确实现
根据你的需求,推荐使用Django 2.0及以上版本支持的「聚合内过滤」写法,这个写法和你原有全量统计逻辑保持一致,没有符合时间范围内活动的用户会被统计为0积分,不会被排除出结果:
import datetime from django.db.models import Sum, Coalesce, Q from django.utils import timezone class UserScoresAPIView(generics.ListAPIView): queryset = User.objects.all() serializer_class = UserScoresSerializer def get_queryset(self): # 构造带时区的截止时间,避免时区不一致导致的统计误差 cutoff_time = timezone.make_aware(datetime.datetime(2020, 10, 1)) queryset = User.objects.annotate( total_pts=Coalesce( Sum( 'activity_user__activity_category__pts', # 仅统计符合时间要求的活动积分 filter=Q(activity_user__activity_datetime__gte=cutoff_time) ), 0 ) ).order_by('-total_pts') return queryset
如果你的业务需要完全排除没有时间范围内活动的用户,可以把过滤条件放在annotate之前,写法如下:
def get_queryset(self): cutoff_time = timezone.make_aware(datetime.datetime(2020, 10, 1)) queryset = User.objects.filter( # 过滤条件要作为关键字参数传入,不要写在字符串里 activity_user__activity_datetime__gte=cutoff_time ).annotate( total_pts=Coalesce(Sum('activity_user__activity_category__pts'), 0) ).order_by('-total_pts') return queryset
如果你的Django版本低于2.0,不支持聚合函数的filter参数,可以先对ActivityLog做过滤聚合,再关联User表获取结果即可。
内容的提问来源于stack exchange,提问作者Bring Coffee Bring Beer
相关产品推荐
相关产品推荐

