如何在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
相关产品推荐
相关产品推荐

