如何按投票总数降序排序Request模型对象(非外键计数方式)
嘿,我明白你的需求——不想每次排序都通过关联Vote表计数,而是想更高效地按投票总数对Request对象排序对吧?这里有个实用的方案,通过给Request添加一个缓存投票数的字段,并自动维护它的数值来解决问题:
解决方案:缓存投票数并自动维护
核心思路是在Request模型里新增一个专门存储投票总数的字段,然后通过Django信号自动更新这个字段的值,这样排序时直接用该字段即可,无需每次查询都做外键计数。
1. 修改模型结构
首先给Request添加vote_count字段,同时建议给Vote加唯一约束,避免同一个用户给同一个请求重复投票:
from django.db import models from django.contrib.auth.models import User class Request(models.Model): title = models.CharField(max_length=255) approved = models.BooleanField(default=False) user = models.OneToOneField(User, on_delete=models.CASCADE) # 新增:存储投票总数的字段,默认0 vote_count = models.IntegerField(default=0) class Vote(models.Model): request = models.ForeignKey(Request, on_delete=models.CASCADE) user = models.ForeignKey(User, on_delete=models.CASCADE) vote_type = models.BooleanField() # 唯一约束:一个用户只能给同一个Request投一次票 class Meta: unique_together = ('request', 'user')
2. 用Django信号自动维护投票数
我们可以利用Django的post_save和post_delete信号,在Vote对象被创建、更新或删除时,自动同步更新对应Request的vote_count:
场景1:只统计总投票数(不管赞成/反对)
from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from .models import Vote @receiver(post_save, sender=Vote) def update_vote_count_on_save(sender, instance, created, **kwargs): # 新建投票时,投票数+1;更新投票类型不影响总数,所以只处理新建场景 if created: instance.request.vote_count += 1 instance.request.save() @receiver(post_delete, sender=Vote) def update_vote_count_on_delete(sender, instance, **kwargs): # 删除投票时,投票数-1 instance.request.vote_count -= 1 instance.request.save()
场景2:统计净票数(赞成+1,反对-1)
如果你的vote_type是用来区分赞成/反对的(比如True代表赞成,False代表反对),可以调整信号逻辑来统计净票数:
@receiver(post_save, sender=Vote) def update_net_vote_count_on_save(sender, instance, created, **kwargs): if created: # 新建投票:赞成加1,反对减1 delta = 1 if instance.vote_type else -1 instance.request.vote_count += delta instance.request.save() else: # 更新投票类型:先减去旧值,再加新值 old_vote = sender.objects.get(pk=instance.pk) old_delta = 1 if old_vote.vote_type else -1 new_delta = 1 if instance.vote_type else -1 instance.request.vote_count += (new_delta - old_delta) instance.request.save() @receiver(post_delete, sender=Vote) def update_net_vote_count_on_delete(sender, instance, **kwargs): # 删除投票:减去对应票数 delta = 1 if instance.vote_type else -1 instance.request.vote_count -= delta instance.request.save()
3. 按投票数降序排序
现在排序变得非常简单直接,性能也比每次计数好很多:
# 获取所有Request,按投票总数降序排列 sorted_requests = Request.objects.order_by('-vote_count') # 可选:只筛选已批准的请求 approved_sorted_requests = Request.objects.filter(approved=True).order_by('-vote_count')
4. 数据迁移与初始化
修改模型后,别忘了执行迁移:
python manage.py makemigrations python manage.py migrate
如果数据库里已有历史数据,需要手动初始化vote_count的值,可以在Django shell中执行:
from django.db.models import Count from .models import Request # 批量更新已有Request的投票数 requests_with_votes = Request.objects.annotate(total_votes=Count('vote')) for req in requests_with_votes: req.vote_count = req.total_votes req.save()
这样就完美解决了你的需求,既避免了重复的外键计数查询,又能高效地按投票数排序。
内容的提问来源于stack exchange,提问作者Benjamin Smith Max
相关产品推荐
相关产品推荐

