一、添加视图函数
打开article/urls.py
,并加入如下代码:
...
# 删除文章
def article_delete(request,id):
# 根据 id 获取需要删除的文章
article = ArticlePost.objects.get(id=id)
# 调用.delete()方法删除文章
article.delete()
# 完成删除后返回文章列表
return redirect("article:article_list")
- 与查询文章类似,因为需要知道具体应该删除哪一篇文章,因此必须传入文章的
id
; - 紧接着调用
.delete()
函数删除数据库中这篇文章的条目; - 删除成功后返回文章列表;
二、添加路由地址
打开article/urls.py
,并加入如下代码:
...
urlpatterns = [
...
# 删除文章
path('article-delete/<int:id>', views.article_delete, name='article_delete'),
]
三、添加删除文章入口
在文章详情的页面进行删除操作,因此修改模板detail.html
:
<div class="container">
<div class="row">
...
<div class="col-12 alert alert-success">
作者:{{ article.author }} · <a href="#" onclick="confirm_delete()">删除文章</a>
</div>
...
</div>
</div>
四、增加弹窗
运行服务器查看,此时就已经可以删除文章了,但是这样存在弊端,删除文章没有提醒,如果不小心点到了,辛辛苦苦写的文章就白写了,所以,应该在删除文章前出现一个弹窗,询问用户是否删除文章。
在这里引入Layer
弹窗组件,layer是一款非常好用的web弹窗组件,具备全方位的解决方案。
官网下载Layer插件:Layer
下载完成之后将里面的layer
文件夹(含有layer.js
的)直接复制到项目的static
文件夹下。
为了未来在所有页面都能使用Layer弹窗功能,在base.html
中通过标签引入:
<body>
...
<!-- bootstrap.js依赖 jquery.js 和 popper.js ,因此在这里引入 -->
<script src="{% static 'jquery/jquery-3.3.1.js' %}"></script>
<!-- 引入layer.js -->
<script src="{% static 'layer/layer.js' %}"></script>
...
</body>
注意:layer
插件依赖jquery
才能正常工作,因此要在jquery
的后面引入layer
;
改写模板detail.html
:
{% block content %}
<!-- 文章详情 -->
<div class="container">
<div class="row">
...
<div class="col-12 alert alert-success">
作者:{{ article.author }} · <a href="#" onclick="confirm_delete()">删除文章</a>
</div>
...
</div>
</div>
<script>//删除文章的函数
function confirm_delete()
{
layer.open
({
//弹窗标题
title: "确认删除",
//正文
content: "确认删除这篇文章吗?",
//点击确定按钮后调用的回调函数
yes: function(index, layero)
{
//指定应当前往的url
location.href='{% url "article:article_delete" article.id %}'
},
})
}</script>
-
<a>
标签增加了onclick
属性,表示在点击链接时调用后面的confirm_delete()
函数; -
confirm_delete()
函数中调用了layer弹窗组件,对弹窗的标题、正文以及确定键进行了定义。location.href
是点击确定键后应该前往的地址,即删除文章的url。 - 通过
onclick
实现了功能逻辑,因此href
链接就不需要在跳转了;
五、提升安全性
使用上面的方法实现删除文章功能虽然难度不大,但是上面的方法是存在安全隐患的。要继续深入探讨,就得提到跨域请求伪造攻击,也称CSRF
攻击(Cross-site request forgery)。
5.1、CSRF攻击
CSRF攻击可以理解为:攻击者盗用了你的身份,以你的名义发送恶意请求。以删除文章为例:
- 用户登录了博客网站A,浏览器记录下这次会话,并保持了登录状态;
- 用户在没有退出登录的情况下,又非常不小心的打开了邪恶的攻击网站B;
- 攻击网站B在页面中植入恶意代码,悄无声息的向博客网站A发送删除文章的请求,此时浏览器误认为是用户在操作,从而顺利的执行了删除操作。
由于浏览器的同源策略,CSRF攻击者并不能得到你的登录数据实际内容,但是可以欺骗浏览器,让恶意请求附上正确的登录数据。
所以这里如果防范CSRF攻击的风险呢?采用的办法是删除文章时用POST方法,并且校验csrf
令牌。
5.2、CSRF令牌
前面提到在Django中提交表单必须加csrf_token
,这个就是CSRF令牌,它防范CSRF攻击的流程如下:
- 当用户访问django站点时,django反馈给用户的表单中有一个隐含字段
csrf_token
,这个值是在服务器端随机生成的,每次都不一样; - 在后端处理POST请求前,django会校验请求的cookie里的
csrf_token
和表单里的csrf_token
是否一致。一致则请求合法,否则这个请求可能是来自CSRF攻击,返回406服务器禁止访问;
由于攻击者并不能得到用户的cookie内容(仅仅是考浏览器转发),所以通常情况下是无法构造出正确的csrf_token
的,从而防范了此类攻击。
5.3、代码实现
修改删除文章的链接,以及点击它时调用的函数
打开templates/article/detail.html
,做如下修改:
{% block content %}
<!-- 文章详情 -->
<div class="container">
<div class="row">
...
<div class="col-12 alert alert-success">
作者:{{ article.author }} · <a href="#" onclick="confirm_safe_delete()">删除文章</a>
</div>
<!-- 新增一个隐藏的表单 -->
<formstyle="display: none;"
id="safe_delete"
action="{% url 'article:article_safe_delete' article.id %}"
method="POST">
{% csrf_token %}
<button type="submit">发送</button>
</form>
...
</div>
</div>
<script>//删除文章的函数
function confirm_safe_delete()
{
layer.open
({
//弹窗标题
title: "确认删除",
//正文
content: "确认删除这篇文章吗?",
//点击确定按钮后调用的回调函数
yes: function(index, layero)
{
$('form#safe_delete button').click();
layer.close(index);
}
})
}</script>
- 点击删除文章链接时,弹出layer弹窗;
- 弹窗不在发起GET请求,而是通过Jquery选择器找到隐藏的表单,并点击发送按钮;
- 表单发起POST请求,并携带了csrf令牌,从而避免了csrf攻击;
接着修改路由地址
打开article/urls.py
,做如下修改:
...
urlpatterns = [
...
# 删除文章
path('article-delete/<int:id>', views.article_safe_delete, name='article_safe_delete'),
]
最后修改删除视图函数
打开article/views.py
,做如下修改:
...
# 删除文章
def article_safe_delete(request,id):
if request.method == 'POST':
article = ArticlePost.objects.get(id=id)
article.delete()
return redirect("article:article_list")
else:
return HttpResponse("仅允许post请求")