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"> 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"> 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"> 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"> reject</font> </a> {% endif %}
方案2:自定义模板过滤器(不需要改原有视图逻辑)
Django默认模板不支持直接给模型方法传参,所以如果不想调整原有视图查询逻辑,写一个简单的自定义过滤器就能实现判断:
- 在你的app目录下新建
templatetags文件夹,文件夹内新建两个空文件:__init__.py、vote_tags.py - 在
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()
- 在模板文件最顶部加载自定义标签:
{% load vote_tags %}
- 修改判断逻辑为:
{% if candidate|check_voted:request.user %} <!-- 后续已投票/未投票代码和上述模板代码一致 -->
额外注意
你代码里多个按钮用了相同的id="Thumbs",HTML规范要求id在页面内唯一,多个元素重复用同一个id会导致前端JS选择器、锚点定位出问题,建议改成class或者拼接候选人id生成唯一id。
内容的提问来源于stack exchange,提问作者Doosu Bere
相关产品推荐
相关产品推荐

