Django如何判断登录用户是否已点赞多对多关联的图片
开发了一个艺术画廊Web应用,以网格形式展示图片,每张图片上显示点赞用户数,已通过AJAX实现动态点赞/取消点赞及实时增减计数功能。现在需要实现:页面加载时,若当前登录用户未点赞某张图片,该图片的点赞按钮显示黑色;若已点赞,则显示蓝色(类似Facebook或Instagram的效果)。
现有代码
Portrait模型(省略部分字段):
class Portrait(models.Model): likes = models.ManyToManyField(User, related_name='like', default=None, blank=True) like_count = models.BigIntegerField(default=0)
views.py中的index视图:
def index(request): paintingObjects = Portrait.objects.all() context = { 'paintingObjects': paintingObjects, } return render(request, "index.html", context)
希望在index.html模板中实现类似如下逻辑(伪代码):
{% for paintingData in paintingObjects %} {% if request.user.is_authenticated %} {% if 当前登录用户在paintingData的'likes'字段中 %} <a role="button" class="like-click" id="{{paintingData.id}}"> <span style="float: left;" class="p-1 mt-1"> <i class="fa fa-thumbs-up" style="font-size:22px;color:blue"></i> </span> </a> {% else 用户尚未点赞该paintingData %} <a role="button" class="like-click" id="{{paintingData.id}}"> <span style="float: left;" class="p-1 mt-1"> <i class="fa fa-thumbs-up" style="font-size:22px;color:black"></i> </span> </a> {% endif %} {% endfor %}
请问如何访问Portrait模型的"likes"字段中的用户列表,并判断当前登录用户是否在其中?
方法一:模板直接判断(简单但需注意性能)
在Django模板中可直接用in运算符判断当前用户是否在paintingData.likes关联列表里,修改模板代码如下:
{% for paintingData in paintingObjects %} {% if request.user.is_authenticated %} {% if request.user in paintingData.likes.all %} <a role="button" class="like-click" id="{{paintingData.id}}"> <span style="float: left;" class="p-1 mt-1"> <i class="fa fa-thumbs-up" style="font-size:22px;color:blue"></i> </span> </a> {% else %} <a role="button" class="like-click" id="{{paintingData.id}}"> <span style="float: left;" class="p-1 mt-1"> <i class="fa fa-thumbs-up" style="font-size:22px;color:black"></i> </span> </a> {% endif %} {% else %} <!-- 未登录用户显示灰色按钮 --> <a role="button" class="like-click" id="{{paintingData.id}}"> <span style="float: left;" class="p-1 mt-1"> <i class="fa fa-thumbs-up" style="font-size:22px;color:gray"></i> </span> </a> {% endif %} {% endfor %}
注意:这种写法会触发N+1查询问题——每遍历一张图片就会额外执行一次查询获取点赞用户列表,图片数量多时会拖慢页面加载速度,建议用下面的优化方案。
方法二:预加载关联数据(优化性能)
在视图中用prefetch_related提前加载所有图片的点赞用户,减少数据库查询次数:
修改views.py的index视图:
def index(request): # 预加载likes关联数据,避免N+1查询 paintingObjects = Portrait.objects.all().prefetch_related('likes') context = { 'paintingObjects': paintingObjects, } return render(request, "index.html", context)
模板代码和方法一完全一致,但此时所有图片的点赞用户已在一次查询中获取完毕,不会产生额外数据库请求,性能更优。
方法三:用annotate标记点赞状态(最优性能)
通过annotate结合Exists子查询,直接在数据库查询阶段给每个Portrait对象添加user_liked字段,标记当前用户是否点赞:
修改views.py:
from django.db.models import Exists, OuterRef def index(request): if request.user.is_authenticated: # 给每个Portrait添加user_liked字段,判断当前用户是否点赞 paintingObjects = Portrait.objects.annotate( user_liked=Exists( request.user.like.filter(pk=OuterRef('pk')) ) ) else: paintingObjects = Portrait.objects.all() context = { 'paintingObjects': paintingObjects, } return render(request, "index.html", context)
模板中直接判断user_liked字段即可,代码更简洁:
{% for paintingData in paintingObjects %} {% if request.user.is_authenticated %} {% if paintingData.user_liked %} <a role="button" class="like-click" id="{{paintingData.id}}"> <span style="float: left;" class="p-1 mt-1"> <i class="fa fa-thumbs-up" style="font-size:22px;color:blue"></i> </span> </a> {% else %} <a role="button" class="like-click" id="{{paintingData.id}}"> <span style="float: left;" class="p-1 mt-1"> <i class="fa fa-thumbs-up" style="font-size:22px;color:black"></i> </span> </a> {% endif %} {% else %} <a role="button" class="like-click" id="{{paintingData.id}}"> <span style="float: left;" class="p-1 mt-1"> <i class="fa fa-thumbs-up" style="font-size:22px;color:gray"></i> </span> </a> {% endif %} {% endfor %}
这种方法性能最优,仅需一次数据库查询就能完成所有判断逻辑,无额外查询开销。
内容的提问来源于stack exchange,提问作者Crandy9

