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令牌存在csrftokenCookie中,通过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
相关产品推荐
相关产品推荐

