Django多对多关系中无法移除用户问题求助
Django多对多关系中无法移除用户问题求助
看起来你遇到的问题主要出在请求方法的选择和CSRF令牌的处理上,我帮你梳理下问题点和对应的解决方案:
核心问题分析
- GET请求不适合处理数据修改:你的
unlike视图只接受GET请求,但GET请求会被浏览器缓存,可能导致移除操作根本没触发;而且按照REST规范,GET仅用于获取数据,修改数据应该用POST请求。 - 缺少CSRF令牌验证:Django默认对POST请求做CSRF保护,如果你的fetch请求没携带CSRF令牌,服务器会直接拒绝请求,导致移除操作失败。
- 多余的
save()调用:对多对多字段执行add()/remove()后,Django会直接操作中间表并保存,不需要额外调用x.save(),这个步骤完全多余。
解决方案:修改视图、模板和JS代码
1. 更新视图函数,改用POST请求
from django.http import JsonResponse from .models import Post def like(request, id): # 限制仅接受POST请求 if request.method != "POST": return JsonResponse({"status": "invalid request method"}, status=400) try: post = Post.objects.get(id=id) post.liked_by.add(request.user) return JsonResponse({"status": "successfully liked"}) except Post.DoesNotExist: return JsonResponse({"status": "post not found"}, status=404) def unlike(request, id): if request.method != "POST": return JsonResponse({"status": "invalid request method"}, status=400) try: post = Post.objects.get(id=id) # 确保用户在点赞列表中再执行移除 if request.user in post.liked_by.all(): post.liked_by.remove(request.user) return JsonResponse({"status": "successfully unliked"}) except Post.DoesNotExist: return JsonResponse({"status": "post not found"}, status=404)
2. 模板中添加CSRF令牌并优化JS逻辑
在模板里加入CSRF令牌,然后修改fetch请求携带令牌并使用POST方法:
{% extends "network/layout.html" %} {% block body %} <!-- 添加CSRF令牌,用于POST请求验证 --> {% csrf_token %} {% for post in posts %} <div>{{post.content}} <button class="like" value="{{post.id}}"> {% if request.user in post.liked_by.all %}unlike{% else %}like{% endif %} </button> </div> {% endfor %} <script> // 获取页面中的CSRF令牌 const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value; const likeButtons = document.querySelectorAll('.like'); likeButtons.forEach(button => { button.addEventListener('click', async (event) => { const clickedButton = event.target; const postId = clickedButton.value; let url, newButtonText; // 判断当前操作类型 if (clickedButton.textContent.trim() === 'like') { url = `/like/${postId}/`; // 请根据你的路由配置调整路径 newButtonText = 'unlike'; } else { url = `/unlike/${postId}/`; newButtonText = 'like'; } try { // 发送POST请求 const response = await fetch(url, { method: 'POST', headers: { 'X-CSRFToken': csrftoken, 'Content-Type': 'application/json' } }); const data = await response.json(); console.log(data.status); // 仅在请求成功时修改按钮文本 clickedButton.textContent = newButtonText; } catch (error) { console.error('操作失败:', error); // 请求失败时恢复按钮原有文本 clickedButton.textContent = clickedButton.textContent.trim() === 'like' ? 'unlike' : 'like'; } }); }); </script> {% endblock %}
3. 检查URL路由配置
确保你的urls.py里的路由正确匹配视图:
from django.urls import path from . import views urlpatterns = [ # 其他路由... path('like/<int:id>/', views.like, name='like'), path('unlike/<int:id>/', views.unlike, name='unlike'), ]
额外小建议
- 加入
try-except捕获Post.DoesNotExist异常,避免无效的帖子ID导致服务器报错; - 使用
async/await让异步代码更易读,同时只有请求成功才修改按钮文本,避免前端显示和后端数据不一致。
这样修改后,应该就能正常移除用户的点赞记录了,你可以试试!
备注:内容来源于stack exchange,提问作者Philip Sebastian
相关产品推荐
相关产品推荐

