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

如何在Django模板中高效判断当前用户是否点赞过帖子?

Django 帖子点赞状态高效判断方案

为什么原方案性能差

你之前用{% if user in post.liked.all %}的方式,会触发N+1查询问题:每渲染一个帖子,都会额外执行一次查询获取该帖子的所有点赞用户;而且当点赞量上千甚至上万时,把所有用户数据拉取到模板中遍历判断,不管是数据库查询还是前端遍历的开销都会急剧上升。

最优方案:用数据库注解直接生成点赞状态

推荐用Django的Exists子查询结合annotate,在数据库层面一次性给每个帖子添加「当前用户是否点赞」的状态字段,这样只需要一次额外的子查询,完全避免N+1,模板里直接用注解字段判断即可。

修改后的视图代码:

from django.db.models import Exists, OuterRef

def posts(request):
    posts = Post.objects.all()
    # 仅对登录用户添加点赞状态注解
    if request.user.is_authenticated:
        posts = posts.annotate(
            is_liked=Exists(
                # 子查询:判断当前帖子是否被当前用户点赞
                Post.objects.filter(id=OuterRef('id'), liked=request.user)
            )
        )
    context = {'posts': posts}
    return render(request, "posts/posts.html", context)

模板中直接使用注解字段:

{% for post in posts %}
    <!-- 帖子内容 -->
    {% if post.is_liked %}
        <button>已点赞</button>
    {% else %}
        <button>点赞</button>
    {% endif %}
{% endfor %}

这个方案的优势:

  • 仅需一次数据库查询(含子查询),性能不受点赞量影响
  • 模板用法简洁,不需要额外处理字典或过滤器
  • 未登录用户默认无is_liked字段,模板会自动走else分支,逻辑自洽

替代方案:生成点赞状态字典

如果你更倾向于在视图中处理状态,也可以生成以帖子ID为键的字典,这种方式比原方案高效,但不如注解方案直接:

视图代码:

def posts(request):
    posts = Post.objects.all()
    context = {'posts': posts}
    if request.user.is_authenticated:
        # 获取当前用户点赞的所有帖子ID列表
        liked_post_ids = request.user.liked_post.values_list('id', flat=True)
        # 生成键为帖子ID,值为布尔值的字典
        context['liked_status'] = {pid: True for pid in liked_post_ids}
    return render(request, "posts/posts.html", context)

模板中判断字典中是否存在当前帖子ID:

{% for post in posts %}
    <!-- 帖子内容 -->
    {% if liked_status and post.id in liked_status %}
        <button>已点赞</button>
    {% else %}
        <button>点赞</button>
    {% endif %}
{% endfor %}

注意:字典的in操作是查询键,比遍历列表快很多,而且只需要一次查询获取点赞ID列表,不会产生N+1问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 16:17:54