You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何按投票总数降序排序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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:52:13