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

Django多对多关系中无法移除用户问题求助

Django多对多关系中无法移除用户问题求助

看起来你遇到的问题主要出在请求方法的选择和CSRF令牌的处理上,我帮你梳理下问题点和对应的解决方案:

核心问题分析

  1. GET请求不适合处理数据修改:你的unlike视图只接受GET请求,但GET请求会被浏览器缓存,可能导致移除操作根本没触发;而且按照REST规范,GET仅用于获取数据,修改数据应该用POST请求。
  2. 缺少CSRF令牌验证:Django默认对POST请求做CSRF保护,如果你的fetch请求没携带CSRF令牌,服务器会直接拒绝请求,导致移除操作失败。
  3. 多余的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 12:19:33