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

Django无需for循环访问多对多关系元素判断用户投票状态

问题原因

你原有代码里的判断{% if request.user == candidate.thumbs.all %}不会生效:candidate.thumbs.all返回的是关联用户的查询集,本质是一组用户对象的集合,和单个用户对象做相等判断永远返回False。

实现方案

全程不需要写for循环遍历多对多数据,两种常用实现方式按需选择即可:

方案1:视图层预聚合查询(性能最优,推荐)

直接在视图查询候选人列表时,用Exists子查询一次性把当前用户对每个候选人的投票状态查出来,不会产生重复查询,候选人数量多的时候性能优势非常明显。
视图代码示例:

from django.db.models import Exists, OuterRef
from .models import PollCandidate

def poll_page(request, poll_id):
    current_user = request.user
    # 一次性查出所有候选人+当前用户的投票状态
    candidate_list = PollCandidate.objects.filter(poll_id=poll_id).annotate(
        user_has_voted=Exists(
            PollCandidate.thumbs.through.objects.filter(
                pollcandidate_id=OuterRef("id"),
                user_id=current_user.id if current_user.is_authenticated else None
            )
        )
    )
    # 其余原有视图逻辑
    return render(request, "myTemplateFile.html", {"candidates": candidate_list})

模板里直接判断预聚合的字段即可,不需要额外传参:

{% if candidate.user_has_voted %}
    <!-- 已投票状态组件,直接放入原有已投票代码即可 -->
    <small style="color:grey; font-size:10px;">you've already voted here</small> 
    <a id="Thumbs" class="btn btn-success btn-sm thumbaction{{poll.id}}{{candidate.id}}"  value="thumbsup" data-toggle="tooltip" title="upVote: I like this candidate" role="button">
        <svg id="{{poll.id}}{{candidate.id}}thumbsup" width="1em" height="1em" viewBox="0 0 16 16" class="up bi bi-hand-thumbs-up d-none"
        xmlns="http://www.w3.org/2000/svg">
            <path fill-rule="evenodd"/>
        </svg><span id="i_like_this"><span class="" style="color:white; font-size:xx-small" id="{{poll.id}}{{candidate.id}}up">{{candidate.thumbsup}} </span></span><font class="" style="color:white; font-size:xx-small;" id="{{poll.id}}{{candidate.id}}approve">&nbsp;approved</font> 
    </a>
    <a id="Thumbs" class="btn btn-danger btn-sm thumbaction{{poll.id}}{{candidate.id}}" value="thumbsdown" data-toggle="tooltip" title="downVote: I dont like this candidate" role="button">
        <svg id="{{poll.id}}{{candidate.id}}thumbsdown" width="1em" height="1em" viewBox="0 0 16 16" class="down bi bi-hand-thumbs-down d-none"
        fill="currentColor" xmlns="http://www.w3.org/2000/svg">
            <path fill-rule="evenodd"/>
        </svg>
        <span class="" style="color:white; font-size:xx-small;" id="{{poll.id}}{{candidate.id}}down">{{candidate.thumbsdown}} </span><font style="color:white; font-size:xx-small;" id="{{poll.id}}{{candidate.id}}reject">&nbsp;rejected</font>  
    </a>
{% else %}
    <!-- 未投票状态组件,直接放入原有未投票代码即可 -->
    <a id="Thumbs" class="btn btn-success btn-sm thumbaction{{poll.id}}{{candidate.id}}"  value="thumbsup" data-toggle="tooltip" title="upVote: I like this candidate" role="button">
        <svg id="{{poll.id}}{{candidate.id}}thumbsup" width="1em" height="1em" viewBox="0 0 16 16" class="up bi bi-hand-thumbs-up"
        xmlns="http://www.w3.org/2000/svg">
            <path fill-rule="evenodd"/>
        </svg><span style="color:white; font-size:xx-small;" id="i_like_this"><span class="d-none" style="color:white; font-size:xx-small;" id="{{poll.id}}{{candidate.id}}up">{{candidate.thumbsup}} </span></span><font class="" style="color:white; font-size:xx-small;" id="{{poll.id}}{{candidate.id}}approve">&nbsp;approve</font> 
    </a>
    <a id="Thumbs" class="btn btn-danger btn-sm thumbaction{{poll.id}}{{candidate.id}}" value="thumbsdown" data-toggle="tooltip" title="downVote: I dont like this candidate" role="button">
        <svg id="{{poll.id}}{{candidate.id}}thumbsdown" width="1em" height="1em" viewBox="0 0 16 16" class="down bi bi-hand-thumbs-down"
        fill="currentColor" xmlns="http://www.w3.org/2000/svg">
            <path fill-rule="evenodd"/>
        </svg>
        <span style="color:white; font-size:xx-small;" class="d-none" id="{{poll.id}}{{candidate.id}}down">{{candidate.thumbsdown}} </span><font class"" style="color:white; font-size:xx-small;" id="{{poll.id}}{{candidate.id}}reject">&nbsp;reject</font>  
    </a>
{% endif %}

方案2:自定义模板过滤器(不需要改原有视图逻辑)

Django默认模板不支持直接给模型方法传参,所以如果不想调整原有视图查询逻辑,写一个简单的自定义过滤器就能实现判断:

  1. 在你的app目录下新建templatetags文件夹,文件夹内新建两个空文件:__init__.py、vote_tags.py
  2. 在vote_tags.py中写入过滤器代码:
from django import template
register = template.Library()

@register.filter
def check_voted(candidate, user):
    if not user.is_authenticated:
        return False
    # 用exists()查询,不需要拉取所有关联用户数据,效率很高
    return candidate.thumbs.filter(id=user.id).exists()
  1. 在模板文件最顶部加载自定义标签:
{% load vote_tags %}
  1. 修改判断逻辑为:
{% if candidate|check_voted:request.user %}
<!-- 后续已投票/未投票代码和上述模板代码一致 -->
额外注意

你代码里多个按钮用了相同的id="Thumbs",HTML规范要求id在页面内唯一,多个元素重复用同一个id会导致前端JS选择器、锚点定位出问题,建议改成class或者拼接候选人id生成唯一id。

内容的提问来源于stack exchange,提问作者Doosu Bere

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 01:18:26