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

如何在Django的Feed页面实现Post点赞/投票功能?

解决Feed页面帖子点赞误操作问题

核心问题分析

你遇到的误点赞其他帖子的情况,本质是前端点赞按钮没有和对应帖子建立唯一绑定,或者后端视图没有正确接收并处理目标帖子的标识(ID/slug)。之前尝试的annotate方法方向正确,但实现细节存在疏漏。

分步解决方案

1. 后端视图修正:正确传递投票状态并处理请求

首先在Feed视图中,通过annotate给每个帖子添加当前用户的投票状态,无需依赖slug参数:

from django.db.models import BooleanField, Case, When
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from .models import Post, Vote

@login_required
def feed(request):
    # 为每个帖子标记当前用户是否已点赞
    posts = Post.objects.annotate(
        is_voted=Case(
            When(votes__user=request.user, then=True),
            default=False,
            output_field=BooleanField()
        )
    ).order_by('-created_at')
    return render(request, 'feed.html', {'posts': posts})

然后编写处理点赞的视图,确保接收帖子的唯一标识(这里用ID):

@login_required
def toggle_vote(request, post_id):
    post = get_object_or_404(Post, id=post_id)
    vote, created = Vote.objects.get_or_create(user=request.user, post=post)
    if not created:
        vote.delete()
    # 返回JSON数据供前端更新状态
    return JsonResponse({
        'success': True,
        'is_voted': created,
        'vote_count': post.votes.count()
    })

在urls.py中配置对应路由:

path('vote/<int:post_id>/', views.toggle_vote, name='toggle_vote'),

2. 前端模板修正:绑定唯一帖子标识

在Feed页面的帖子循环中,给每个点赞按钮添加data-post-id属性,确保点击事件只对应当前帖子:

{% for post in posts %}
<div class="post-item">
    <h4>{{ post.title }}</h4>
    <!-- 点赞按钮 -->
    <button class="vote-btn" 
            data-post-id="{{ post.id }}" 
            data-is-voted="{{ post.is_voted }}">
        {% if post.is_voted %}
            <span>取消点赞</span>
        {% else %}
            <span>点赞</span>
        {% endif %}
        <span class="vote-count">({{ post.votes.count }})</span>
    </button>
</div>
{% endfor %}

用JavaScript绑定点击事件,基于data-post-id发送请求并更新当前按钮状态:

// 绑定所有点赞按钮的点击事件
document.querySelectorAll('.vote-btn').forEach(btn => {
    btn.addEventListener('click', async function() {
        const postId = this.dataset.postId;
        const csrfToken = getCookie('csrftoken');
        
        const response = await fetch(`/vote/${postId}/`, {
            method: 'POST',
            headers: {
                'X-CSRFToken': csrfToken,
                'Content-Type': 'application/json'
            }
        });
        
        const data = await response.json();
        // 更新当前按钮的显示状态和点赞数
        this.dataset.isVoted = data.is_voted;
        this.querySelector('span:first-child').textContent = data.is_voted ? '取消点赞' : '点赞';
        this.querySelector('.vote-count').textContent = `(${data.vote_count})`;
    });
});

// 辅助函数:获取CSRF Token
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

3. 之前尝试的问题复盘

  • 给Feed视图加slug参数导致404:Feed是列表页,不需要单个帖子的slug,强行添加会破坏路由匹配逻辑。
  • annotate触发TypeError:错误传递了slug参数,正确做法是基于当前登录用户的投票记录做Case/When判断,无需依赖slug。
  • 循环内脚本的误操作bug:脚本未绑定到具体按钮,点击时会选中第一个匹配元素,必须通过data-*属性绑定帖子唯一标识。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 03:31:36