如何用Django与JavaScript优化点赞按钮并修复现有问题?
Django异步点赞功能问题修复方案
需求说明
- 实现带图标的「点赞(Like)」和「取消点赞(Unlike)」按钮,用户点击任意帖子按钮可切换点赞状态
- 通过JavaScript异步调用
fetch通知服务器更新点赞数,无需刷新页面即可更新页面上的帖子点赞数
现有问题
- 处于取消点赞图标状态时,无法切换回点赞图标
- 点赞某一帖子后,所有帖子的点赞数都会同步更新
- 当前代码较为初级,不够规范
现有代码
Django模板HTML文件
{% if post.id in postLiked %} <button class="btn btn-primary fa fa-thumbs-down" id="{{ post.id }}" onclick="likeOrUnlike('{{ post.id }}', '{{ postLiked }}')"></button> {% else %} <button class="btn btn-primary fa fa-thumbs-up" id="{{ post.id }}" onclick="likeOrUnlike('{{ post.id }}', '{{ postLiked }}')"></button> {% endif %} <div class="container"> <div class="row justify-content-center"> <h3 class="col-4">Like: {{ likes.count }}</h3> </div> </div>
models.py文件
class LikeOrUnlike(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='likeByUser') post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='postLiked') def __str__(self): return f'{self.user} liked this {self.post}'
urls.py文件
path("", views.index, name="index"), path('like/<int:post_id>', views.like,name='like'), path('unlike/<int:post_id>', views.unlike,name='unlike')
views.py文件
def index(request): likes = LikeOrUnlike.objects.all() postLiked = [] try: for like in likes: if like.user.id == request.user.id: postLiked.append(like.post.id) except: postLiked = [] return render(request, "network/index.html",{ 'likes':likes, 'postLiked': postLiked }) # 点赞功能 def like(request, post_id): post = Post.objects.get(pk=post_id) user = User.objects.get(pk=request.user.id) likePost = LikeOrUnlike(user=user, post=post) likePost.save() return JsonResponse({"message": "Liked"}) # 取消点赞功能 def unlike(request, post_id): post = Post.objects.get(pk=post_id) user = User.objects.get(pk=request.user.id) unlikePost = LikeOrUnlike.objects.filter(user=user, post=post) unlikePost.delete() return JsonResponse({"message": "Unliked"})
JavaScript代码
// 点赞/取消点赞逻辑 function likeOrUnlike(id, postLiked){ const btn = document.getElementById(`${id}`); btn.classList.remove('fa-thumbs-up') btn.classList.remove('fa-thumbs-down') if(postLiked.indexOf(id) >= 0){ var liked = true; }else{ var liked = false; } if(liked === true){ fetch(`/unlike/${id}`) .then(response => response.json) .then(result => { btn.classList.add("fa-thumbs-up") }) }else{ fetch(`/like/${id}`) .then(response => response.json) .then(result => { btn.classList.add("fa-thumbs-down") }) } liked = !liked }
问题修复方案
1. 解决图标无法切换的问题
问题出在:
- 模板里传递的
postLiked是列表转成的字符串,indexOf无法正确判断状态 - JavaScript中
response.json未加括号调用,无法获取返回数据 - 切换状态后未更新本地点赞记录,下次判断仍用旧值
解决办法:
- 模板用
json_script传递postLiked,避免字符串解析问题:
按钮去掉内联的{{ postLiked|json_script:"post-liked-data" }}postLiked参数,只传帖子ID:{% if post.id in postLiked %} <button class="btn btn-primary fa fa-thumbs-down" data-post-id="{{ post.id }}"></button> {% else %} <button class="btn btn-primary fa fa-thumbs-up" data-post-id="{{ post.id }}"></button> {% endif %} - 重写JavaScript逻辑,用事件委托处理点击,修复
fetch错误:document.addEventListener('DOMContentLoaded', () => { const postLiked = JSON.parse(document.getElementById('post-liked-data').textContent); document.body.addEventListener('click', (e) => { const btn = e.target.closest('[data-post-id]'); if (!btn) return; const postId = btn.dataset.postId; const isLiked = postLiked.includes(postId); btn.classList.remove('fa-thumbs-up', 'fa-thumbs-down'); fetch(isLiked ? `/unlike/${postId}` : `/like/${postId}`, { method: 'POST', headers: { 'X-CSRFToken': getCookie('csrftoken') // Django必须传CSRF令牌 } }) .then(res => res.json()) .then(() => { if (isLiked) { postLiked.splice(postLiked.indexOf(postId), 1); btn.classList.add('fa-thumbs-up'); } else { postLiked.push(postId); btn.classList.add('fa-thumbs-down'); } updateLikeCount(postId); }) .catch(err => console.error('操作失败:', err)); }); // 获取CSRF令牌 function getCookie(name) { let cookieValue = null; if (document.cookie) { const cookies = document.cookie.split(';'); for (const cookie of cookies) { const [key, value] = cookie.trim().split('='); if (key === name) { cookieValue = decodeURIComponent(value); break; } } } return cookieValue; } });
2. 解决所有帖子点赞数同步更新的问题
问题出在模板里用了{{ likes.count }},这是全局所有点赞的总数,不是单帖的点赞数。
解决办法:
- 模板里显示单帖的点赞数,利用模型的
related_name获取:<h3 class="col-4 like-count" data-post-id="{{ post.id }}">Like: {{ post.postLiked.count }}</h3> - 新增获取单帖点赞数的接口:
urls.py添加路由:
views.py添加视图:path('like-count/<int:post_id>', views.like_count, name='like-count'),def like_count(request, post_id): post = get_object_or_404(Post, pk=post_id) return JsonResponse({"count": post.postLiked.count()}) - JavaScript添加更新点赞数的函数:
function updateLikeCount(postId) { fetch(`/like-count/${postId}`) .then(res => res.json()) .then(data => { const countEl = document.querySelector(`.like-count[data-post-id="${postId}"]`); countEl.textContent = `Like: ${data.count}`; }); }
3. 代码规范优化
视图层优化
- 直接用
request.user获取当前用户,避免重复查询 - 用
values_list批量获取点赞的帖子ID,提升效率 - 用
get_object_or_404处理不存在的帖子,避免报错 - 增加登录校验,防止未登录用户操作:
def index(request): postLiked = [] if request.user.is_authenticated: # 直接查询当前用户点赞的所有帖子ID postLiked = list(LikeOrUnlike.objects.filter(user=request.user).values_list('post_id', flat=True)) posts = Post.objects.all() return render(request, "network/index.html", { 'posts': posts, 'postLiked': postLiked }) def like(request, post_id): if not request.user.is_authenticated: return JsonResponse({"error": "请先登录"}, status=401) post = get_object_or_404(Post, pk=post_id) # 避免重复点赞 if not LikeOrUnlike.objects.filter(user=request.user, post=post).exists(): LikeOrUnlike.objects.create(user=request.user, post=post) return JsonResponse({"message": "点赞成功"}) def unlike(request, post_id): if not request.user.is_authenticated: return JsonResponse({"error": "请先登录"}, status=401) post = get_object_or_404(Post, pk=post_id) LikeOrUnlike.objects.filter(user=request.user, post=post).delete() return JsonResponse({"message": "取消点赞成功"})
模板层优化
- 用循环渲染帖子,避免重复代码,给每个帖子添加容器:
{% for post in posts %} <div class="post-item"> <!-- 帖子内容区域 --> <p>{{ post.content }}</p> <!-- 点赞按钮 --> {% if post.id in postLiked %} <button class="btn btn-primary fa fa-thumbs-down" data-post-id="{{ post.id }}"></button> {% else %} <button class="btn btn-primary fa fa-thumbs-up" data-post-id="{{ post.id }}"></button> {% endif %} <!-- 点赞数 --> <div class="container"> <div class="row justify-content-center"> <h3 class="col-4 like-count" data-post-id="{{ post.id }}">Like: {{ post.postLiked.count }}</h3> </div> </div> </div> {% endfor %} <!-- 传递点赞记录 --> {{ postLiked|json_script:"post-liked-data" }}
JavaScript优化
- 用事件委托代替内联
onclick,代码更解耦,新增帖子无需重新绑定事件 - 用箭头函数简化代码,逻辑更清晰
- 统一错误处理,方便调试
内容的提问来源于stack exchange,提问作者Sherpa Coutinho
相关产品推荐
相关产品推荐

