如何在无DRF的Django项目中,通过点击事件更新Post模型的likes字段
Django实现帖子点赞功能(无需DRF)
1. 确认Post模型的likes字段配置
首先确保你的Post模型里有正确的likes字段,建议用整数类型并设置默认值为0,加上数据库索引优化查询:
from django.db import models class Post(models.Model): # 其他业务字段示例 title = models.CharField(max_length=200) content = models.TextField() likes = models.IntegerField(default=0, db_index=True) # 点赞数字段 created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return self.title
2. 编写点赞处理视图
用Django原生函数视图处理点赞请求,必须使用原子更新操作避免并发场景下的计数错误:
from django.shortcuts import get_object_or_404, redirect from django.http import JsonResponse from django.db.models import F from .models import Post def like_post(request, post_id): # 确保目标帖子存在 post = get_object_or_404(Post, id=post_id) # 原子更新点赞数(避免多用户同时操作时的计数偏差) Post.objects.filter(id=post_id).update(likes=F('likes') + 1) # 根据请求类型返回响应 if request.headers.get('X-Requested-With') == 'XMLHttpRequest': # AJAX请求返回JSON,用于无刷新更新页面 return JsonResponse({'success': True, 'new_likes': post.likes + 1}) else: # 普通表单提交,重定向回帖子详情页(替换为你的实际路由名称) return redirect('post_detail', post_id=post_id)
3. 配置URL路由
在应用的urls.py中添加点赞路由:
from django.urls import path from . import views urlpatterns = [ # 其他路由示例 path('post/<int:post_id>/', views.post_detail, name='post_detail'), # 点赞功能路由 path('post/<int:post_id>/like/', views.like_post, name='like_post'), ]
4. 模板中实现点赞按钮
提供两种实现方式,根据需求选择:
方式一:普通表单提交(页面刷新)
适合简单场景,点击按钮后页面刷新:
<!-- 在帖子详情页或列表项中 --> <form action="{% url 'like_post' post.id %}" method="post"> {% csrf_token %} <button type="submit" class="like-btn">点赞 ({{ post.likes }})</button> </form>
方式二:AJAX无刷新提交(用户体验更好)
点击按钮后不刷新页面,直接更新点赞数:
<!-- 按钮部分 --> <button id="like-btn-{{ post.id }}" data-post-id="{{ post.id }}" class="like-btn"> 点赞 ({{ post.likes }}) </button> <!-- JavaScript部分,建议放在页面底部或base模板中 --> <script> // 监听所有点赞按钮的点击事件 document.querySelectorAll('.like-btn').forEach(btn => { btn.addEventListener('click', function(e) { e.preventDefault(); const postId = this.dataset.postId; // 发送POST请求 fetch(`/post/${postId}/like/`, { method: 'POST', headers: { 'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value, 'X-Requested-With': 'XMLHttpRequest' } }) .then(response => response.json()) .then(data => { if (data.success) { // 更新按钮文字显示最新点赞数 this.textContent = `点赞 (${data.new_likes})`; } }); }); }); </script>
注意:模板中必须包含CSRF令牌,可在页面顶部或base模板中添加{% csrf_token %}。
5. 可选:防止重复点赞
如果需要限制同一用户只能点赞一次,可以添加关联模型记录点赞关系:
from django.contrib.auth.models import User class Like(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) post = models.ForeignKey(Post, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) class Meta: unique_together = ('user', 'post') # 确保用户对同一帖子只能点赞一次
然后修改视图函数,支持点赞/取消点赞切换:
def like_post(request, post_id): # 未登录用户跳转到登录页 if not request.user.is_authenticated: return redirect('login') post = get_object_or_404(Post, id=post_id) # 检查用户是否已点赞,不存在则创建,存在则删除 liked, created = Like.objects.get_or_create(user=request.user, post=post) if created: # 新增点赞,增加计数 Post.objects.filter(id=post_id).update(likes=F('likes') + 1) new_likes = post.likes + 1 else: # 取消点赞,减少计数 Post.objects.filter(id=post_id).update(likes=F('likes') - 1) new_likes = post.likes - 1 liked.delete() # 返回响应 if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({ 'success': True, 'new_likes': new_likes, 'is_liked': created # 告诉前端当前是否已点赞 }) else: return redirect('post_detail', post_id=post_id)
模板中可以根据用户是否已点赞动态显示按钮文字:
{% if request.user.is_authenticated %} {% if post.like_set.filter(user=request.user).exists %} <button id="like-btn-{{ post.id }}" data-post-id="{{ post.id }}" class="like-btn"> 取消点赞 ({{ post.likes }}) </button> {% else %} <button id="like-btn-{{ post.id }}" data-post-id="{{ post.id }}" class="like-btn"> 点赞 ({{ post.likes }}) </button> {% endif %} {% else %} <a href="{% url 'login' %}" class="like-link">登录后点赞</a> ({{ post.likes }}) {% endif %}
内容的提问来源于stack exchange,提问作者JungHwanPark
相关产品推荐
相关产品推荐

