Django中如何为带slug参数的URL编写JavaScript Fetch请求?
解决Django中Fetch API实现无刷新点赞的URL与CSRF问题
问题背景
作为Django和异步开发新手,正在优化留言板应用,希望用Fetch API实现无刷新点赞,目前卡在Fetch请求的URL构造(带slug参数)和CSRF令牌处理上。
一、正确构造Fetch请求的URL
你的PostLike视图URL规则为path('like/<slug:slug>/', ..., name='post_like'),有两种可靠的URL构造方式:
方式1:模板直接生成完整URL传递给JS
修改模板中按钮的onclick事件,把Django生成好的完整点赞URL传入函数,同时将按钮type改为button避免表单默认提交刷新页面:
<button class="btn-like" type="button" onclick="like('{% url 'post_like' post.slug %}')">
方式2:JS中用slug拼接URL
如果要在JS内拼接URL,先把帖子slug传递给函数:
<button class="btn-like" type="button" onclick="like('{{ post.slug }}')">
再在JS中构造URL:
function like(post_slug) { const url = `/like/${post_slug}/`; // 注意末尾斜杠要和urls.py中的规则匹配 // 后续代码... }
二、处理CSRF令牌
Django要求POST请求必须携带CSRF令牌,Fetch API需要手动将令牌添加到请求头:
步骤1:从Cookie中读取CSRF令牌
Django会将CSRF令牌存在页面Cookie中,通过以下工具函数获取:
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; }
步骤2:在Fetch请求中添加CSRF头
修改Fetch配置,将令牌放入请求头:
const csrftoken = getCookie('csrftoken'); fetch(url, { method: "POST", headers: { 'X-CSRFToken':': csrftoken, 'Accept': 'application/json' } })
三、完整代码修改示例
1. 帖子详情模板片段修改
<div class="row"> <div class="col-1"> {% if user.is_authenticated %} <strong> {% if liked %} <button class="btn-like" type="button" onclick="like('{% url 'post_like' post.slug %}')"> <i class="like-icon fas fa-thumbs-up"></i> </button> <span class="likes-count">{{ post.number_of_likes }}</span> {% else %} <button class="btn-like" type="button" onclick="like('{% url 'post_like' post.slug %}')"> <i class="like-icon far fa-thumbs-up"></i> </button> <span class="likes-count">{{ post.number_of_likes }}</span> {% endif %} </strong> {% else %} <strong class="text-secondary"><i class="far fa-thumbs-up"></i> <span class="likes-count">{{ post.number_of_likes }}</span></strong> {% endif %} </div>
注:将
id改为class,避免页面存在多个帖子时DOM选择器冲突
2. 完整JavaScript代码
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; } function like(url) { const csrftoken = getCookie('csrftoken'); // 通过事件对象定位当前按钮对应的图标和计数元素 const button = event.target.closest('.btn-like'); const likeIcon = button.querySelector('.like-icon'); const likeCount = button.nextElementSibling; fetch(url, { method: "POST", headers: { 'X-CSRFToken': csrftoken, 'Accept': 'application/json' } }) .then(response => { if (!response.ok) throw new Error('请求失败'); return response.json(); }) .then(data => { likeCount.textContent = data.likes; likeIcon.className = data.liked ? "like-icon fas fa-thumbs-up" : "like-icon far fa-thumbs-up"; }) .catch(() => alert("Unable to like/unlike post.")); }
3. 视图层无需修改
你的PostLike视图已正确返回JsonResponse,保持原样即可。
关键注意事项
- 按钮必须设置
type="button",避免触发表单默认提交行为 - 多帖子页面不要用
id选择DOM元素,改用class或事件对象定位 - CSRF令牌必须从Cookie读取并添加到请求头,否则Django会拒绝请求
- URL格式要和
urls.py中的规则完全匹配(比如末尾的斜杠)
内容的提问来源于stack exchange,提问作者Mark Hewitt
相关产品推荐
相关产品推荐

