基于带参数子查询实现Django Event关联有效报名数据统计标注
你提到的给Registration新增字段标识有效报名的方案完全可实现,也是生产环境优先推荐的解决方案,具体实现和替代方案如下:
方案1:新增is_valid标识字段(推荐,性能最优)
实现步骤
- 给
Registration模型新增有效性标识字段:
class Registration(models.Model): # 原有字段保持不变 is_valid = models.BooleanField(default=False, verbose_name="是否为容量内有效报名")
- 配置有效性判定逻辑:
- 新建报名时:先查询对应活动当前的有效报名数,小于活动容量时将新报名的
is_valid设为True,否则设为False - 活动容量被修改、或者已有有效报名被取消时,触发重算逻辑:将超出容量的报名标记为无效,有空缺时按报名顺序将排队的报名标记为有效
可以通过重写模型save/delete方法、或者使用Django信号(post_save/post_delete)触发上述逻辑。
- 修改原有Admin统计代码,仅统计有效报名:
def get_queryset(self, request): queryset = super(EventAdmin, self).get_queryset(request) return queryset.annotate( nr_registrations = Count('registration', filter=Q(registration__is_valid=True)), nr_bus = Count('registration', filter=Q(registration__is_valid=True, registration__uses_bus=True)), nr_vegan = Count('registration', filter=Q(registration__is_valid=True, registration__is_vegan=True)), )
该方案逻辑简单,查询性能高,适合大多数业务场景。
方案2:无需新增字段,用窗口函数实时统计
如果不想修改表结构,可以用Django支持的窗口函数实现实时筛选有效报名后聚合:
from django.db.models import Count, Q, F, Window from django.db.models.functions import RowNumber def get_queryset(self, request): queryset = super(EventAdmin, self).get_queryset(request) # 给每个活动的报名按报名顺序排序并编号 valid_regs = Registration.objects.annotate( rank=Window( expression=RowNumber(), partition_by=F('event_id'), order_by=F('id').asc() ) ).filter(rank <= F('event__capacity')).values('id') # 仅统计容量范围内的有效报名 return queryset.annotate( nr_registrations=Count('registration', filter=Q(registration__id__in=valid_regs)), nr_bus=Count('registration', filter=Q(registration__id__in=valid_regs, registration__uses_bus=True)), nr_vegan=Count('registration', filter=Q(registration__id__in=valid_regs, registration__is_vegan=True)), )
该方案无需修改模型,适合数据量较小、统计频次不高的场景,缺点是每次查询都需要做窗口计算,性能低于方案1。
内容的提问来源于stack exchange,提问作者brahms
相关产品推荐
相关产品推荐

