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

Django视图JsonResponse无法被JQuery Ajax成功函数访问的问题

Django音乐人点赞功能Ajax请求异常排查与解决

初始代码与404错误

视图代码

class MusicianLikesView(DetailView):
    model = Musician
    template_name = "guitar_blog/blog_detail.html"

    def post(self, request, **kwargs):
        song = self.get_object()
        user_id = request.user.id
        user_username = request.user.username

        if request.user.is_authenticated:
            if "Like1" in request.POST and user_username not in song.music_1_users.keys():
                song.music_1_votes += 1
                song.music_1_users[user_username] = user_id
                song.save()
                return JsonResponse({"likes": song.music_1_votes})

            elif "Unlike1" in request.POST and user_username in song.music_1_users.keys():
                song.music_1_votes -= 1
                song.music_1_users.pop(user_username)
                song.save()
                return JsonResponse({"likes": song.music_1_votes})

URL配置

path('ajax/', MusicianLikesView.as_view(), name="song_likes"),

模板代码

<form method="post" class="voting-setup top_song">
    {% csrf_token %} {% if user.get_username in musician.music_1_users %}
    <button name="Unlike1" type="submit" class="like-button">
        <i class="fa-solid fa-guitar icon"
            ><span class="like-text">Unlike</span></i
        >
    </button>
    {% else %}
    <button name="Like1" type="submit" class="like-button">
        <i class="fa-solid fa-guitar icon"
            ><span class="like-text">Like</span></i
        >
    </button>
    {% endif %}
</form>
<span class="after-like first_song_likes">Liked {{ musician.music_1_votes }}</span>

JavaScript代码

$(document).on("submit", ".top_song", function (event) {
    event.preventDefault();
    $.ajax({
        type: "POST",
        url: "ajax/",
        data: {
            csrfmiddlewaretoken: "{{ csrf_token }}",
        },
        success: function (response) {
            console.log(response);
        },
        error: function (re) {
            console.log(re);
        }
    });
});

此时控制台报错:

POST http://127.0.0.1:8000/guitar_blog/20/ajax/ 404 (Not Found)

修改为函数视图后仍404

修改后的视图

def musician_likes_view(request, pk):
    song = Musician.objects.get(pk=pk)
    user_id = request.user.id
    user_username = request.user.username

    if request.user.is_authenticated:
        if "Like1" in request.POST and user_username not in song.music_1_users.keys():
            song.music_1_votes += 1
            song.music_1_users[user_username] = user_id
            song.save()
            return HttpResponse(song.music_1_votes)

        elif "Unlike1" in request.POST and user_username in song.music_1_users.keys():
            song.music_1_votes -= 1
            song.music_1_users.pop(user_username)
            song.save()
            return HttpResponse(song.music_1_votes)

修改后的URL配置

path('ajax/', musician_likes_view, name="song_likes"),

控制台仍报相同404错误。

更新URL与代码后出现403错误

更新后的URL配置

path('guitar_blog/<int:pk>/ajax/', MusicianLikesView.as_view(), name="song_likes"),

更新后的视图

if request.user.is_authenticated:
    if "Like1" == request.POST["name"] and user_username not in song.music_1_users.keys():
        song.music_1_votes += 1
        song.music_1_users[user_username] = user_id
        song.save()
        return JsonResponse({"likes": song.music_1_votes})

    elif "Unlike1" == request.POST["name"] and user_username in song.music_1_users.keys():
        song.music_1_votes -= 1
        song.music_1_users.pop(user_username)
        song.save()
        return JsonResponse({"likes": song.music_1_votes})

更新后的JavaScript代码

$(".top_song").submit((event) => {
    event.preventDefault();
    $.ajax({
        type: "POST",
        url: "ajax/",
        data: {
            "name": $(".top_song button").attr("name"),
            csrfmiddlewaretoken: "{{ csrf_token }}",
        },
        success: function (response) {
            console.log(response["likes"]);
        },
        error: function (re) {
            console.log(re);
        }
    });
});

此时控制台报错:

jquery.min.js:2          POST http://127.0.0.1:8000/guitar_blog/20/ajax/ 403 (Forbidden)

网络响应提示CSRF验证失败,原因是“POST的CSRF令牌长度不正确”。


问题分析与解决方案

1. 404错误的根源

  • URL层级与参数缺失:初始URL未包含<int:pk>参数,无论类视图还是函数视图都需要通过主键获取音乐人对象,路由无法匹配带pk的请求路径,导致404。如果主路由是path('guitar_blog/', include('guitar_blog.urls')),子路由必须包含pk参数才能匹配guitar_blog/20/ajax/这类路径。

2. 403错误的根源

  • CSRF令牌未正确渲染:静态JS文件中无法解析Django模板标签{{ csrf_token }},导致传递的令牌是字符串字面量而非真实令牌,触发CSRF验证失败。

完整修复步骤

步骤1:修正URL配置(guitar_blog/urls.py)

无论类视图还是函数视图,路由必须包含pk参数:

# 类视图版本
path('<int:pk>/ajax/', MusicianLikesView.as_view(), name="song_likes")

# 函数视图版本
path('<int:pk>/ajax/', musician_likes_view, name="song_likes")

步骤2:修复CSRF令牌传递

不要在静态JS文件中使用模板标签,改用以下两种方法:

  • 方法一:从表单隐藏字段获取令牌
    模板中的{% csrf_token %}会生成隐藏input,通过选择器获取其值:
$(".top_song").submit(function(event) {
    event.preventDefault();
    const csrfToken = $(this).find('[name="csrfmiddlewaretoken"]').val();
    $.ajax({
        type: "POST",
        url: "ajax/",
        data: {
            "name": $(this).find('button').attr("name"),
            csrfmiddlewaretoken: csrfToken,
        },
        success: function(response) {
            console.log(response.likes);
            $(".first_song_likes").text(`Liked ${response.likes}`);
        },
        error: function(re) {
            console.log(re);
        }
    });
});
  • 方法二:从Cookie读取令牌
    Django将CSRF令牌存在csrftoken Cookie中,通过JS读取:
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;
}

$(".top_song").submit(function(event) {
    event.preventDefault();
    const csrfToken = getCookie('csrftoken');
    $.ajax({
        type: "POST",
        url: "ajax/",
        data: {
            "name": $(this).find('button').attr("name"),
            csrfmiddlewaretoken: csrfToken,
        },
        success: function(response) {
            console.log(response.likes);
            $(".first_song_likes").text(`Liked ${response.likes}`);
        },
        error: function(re) {
            console.log(re);
        }
    });
});

步骤3:完善视图逻辑

添加未登录、对象不存在及无效请求的响应处理:

# 类视图示例
class MusicianLikesView(DetailView):
    model = Musician
    template_name = "guitar_blog/blog_detail.html"

    def post(self, request, **kwargs):
        song = self.get_object()
        if not request.user.is_authenticated:
            return JsonResponse({"error": "请先登录"}, status=401)

        user_username = request.user.username
        if "Like1" in request.POST and user_username not in song.music_1_users.keys():
            song.music_1_votes += 1
            song.music_1_users[user_username] = request.user.id
            song.save()
            return JsonResponse({"likes": song.music_1_votes})

        elif "Unlike1" in request.POST and user_username in song.music_1_users.keys():
            song.music_1_votes -= 1
            song.music_1_users.pop(user_username)
            song.save()
            return JsonResponse({"likes": song.music_1_votes})
        
        return JsonResponse({"likes": song.music_1_votes}, status=200)

步骤4:修正请求参数判断

视图中无需通过request.POST["name"]判断,保持"Like1" in request.POST即可,或者匹配AJAX传递的name参数:

# 匹配AJAX传递的name参数的写法
if request.POST.get('name') == 'Like1' and user_username not in song.music_1_users.keys():
    # 逻辑代码

内容的提问来源于stack exchange,提问作者WebStudent2011

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 21:06:24