不安全的方式
首先增加一个视图函数:
article/views.py
...
#删文章
def article_delete(request,id):
#根据id获取需要删除的文章
article = ArticlePost.objects.get(pk=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
:
templates/article/detail.html
...
<!-- 文章详情 -->
<div class="container">
<div class="row">
...
<div class="col-12 alert alert-success">作者:{{ article.author }}
· <a href="{% url "article:article_delete" article.id %}">删除文章</a>
</div>
...
</div>
</div>
...
这里增加了一个调用
article_delete
视图函数的链接,并且将article.id
传递进去。
运行开发服务器,可以发现已经能够正常的删除文章了:
增加弹窗
功能已经实现了,增加弹窗防止误点。
Layer弹窗组件是一款备受青睐的web弹层组件,具备全方位的解决方案。 Layer官网 下载Layer,为了未来在所有页面都能使用Layer弹窗功能,在base.html中通过标签引入:
templates/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
:
templates/article/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>
<script>
// 删除文章的函数
function confirm_delete() {
// 调用layer弹窗组件
layer.open({
// 弹窗标题
title: "确认删除",
// 正文
content: "确认删除这篇文章吗?",
// 点击确定按钮后调用的回调函数
yes: function(index, layero) {
// 指定应当前往的 url
location.href='{% url "article:article_delete" article.id %}'
},
})
}
</script>
{% endblock content %}
<a>
标签中增加了onclick
属性,表示在点击链接时调用后面的confirm_delete()
函数。confirm_delete()
函数中调用了layer弹窗组件,对弹窗的标题、正文以及确定键进行了定义。location.href
是点击确定键后应该前往的地址,即删除文章的url。- 通过
onclick
实现了功能逻辑,因此href
链接就不需要再跳转了。
保存所有文件后刷新页面:
安全的方式
CSRF攻击的防范。记住一条,凡是重要的数据操作,都应该考虑带有 csrf 令牌的 POST 请求;或者更简单的方法,数据查询用 GET,数据更改用 POST。
修改detail.html
templates/article/detail.html
...
<!-- · <a href="#" onclick="confirm_delete()">删除文章</a> -->
· <a href="#" onclick="confirm_safe_delete()">删除文章</a>
<!-- 新增一个隐藏的表单 -->
<form
style="display:none;"
id="safe_delete"
action="{% url 'article:article_safe_delete' article.id %}"
method="POST"
>
{% csrf_token %}
<button type="submit">发送</button>
</form>
...
<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 攻击
接着添加表单提交的url:
article/urls.py
...
urlpatterns = [
...
# 安全删除文章
path(
'article-safe-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请求")
视图一定要限制为 POST 请求,即if request.method == 'POST'
必须有。