前文我们实现了分别实现了显示文章,创建文章等功能。
本篇我们实现删除文章的功能。
删除文章单从功能实现上面来说很简单,视图函数如下:
- # 删文章
- def article_delete(request, id):
- # 根据 id 获取需要删除的文章
- article = Article.objects.get(id=id)
- # 调用.delete()方法删除文章
- article.delete()
- # 完成删除后返回文章列表
- return redirect("list")
然后修改下路由配置文档
- urlpatterns = [
- path('admin/', admin.site.urls),
- path('hello/', views.hello),
- re_path(r'^$', views.article_list),
- path('list/', views.article_list, name='list'), # 展示文章
- path('detail/
/' , views.article_detail, name='detail'), # 文章详情 - path('create/', views.article_create, name='create'), # 写文章
- # 增加生删除文章
- path('delete/
/' , views.article_delete, name='delete'), - ]
最后修改模板,在详情页面detail.html增加一个删除文章的按钮。
- <div class="container">
-
-
- <h1 class="col-12 mt-4 mb-4">{{ article.title }}h1>
- <div class="col-12 alert alert-primary">
- <div class="col-12">
- <a>作者:{{ article.author }}a>
-  
- <a>{{ article.created|date:'Y-m-d H:i:s' }}a>
-  
- <a href="{% url "delete" article.id %}">删除文章a> #增加此项
- div>
- div>
-
- <div class="col-12">
- <p>{{ article.body }}p>
- div>
- div>
运行服务器之后,可以看到已经增加了删除文章按钮。

上面实现了一个简单的功能,不过有一个隐患,就是如果是不小心点到了删除按钮,可能连后悔的机会都没了。
一般这种涉及到数据直接清除的动作,要加一个弹窗确认的动作,二次确认后才能直接删除数据库数据。
实现这个功能我们可以使用Bootstrap的模态框的组件。
模态框(Modal)是覆盖在父窗体上的子窗体。通常,目的是显示来自一个单独的源的内容,可以在不离开父窗体的情况下有一些互动。子窗体可提供信息交互等。
在detail.html中之前新增加的那行删除文章代码进行如下修改:
"#" data-bs-toggle="modal" data-bs-target="#myModal">删除文章
增加了模态框效果,点击此链接会跳弹出名为myModal的模态框。
接着我们实现myModal模态框删除文章的功能。
- <div class="modal fade" id="myModal">
- <div class="modal-dialog modal-dialog-centered modal-sm">
- <div class="modal-content">
-
- <div class="modal-header">
- <h4 class="modal-title">确认删除h4>
- <button type="button" class="btn-close" data-bs-dismiss="modal">button>
- div>
-
- <div class="modal-body">
- 确认删除文章?
- div>
-
- <div class="modal-footer">
- <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消button>
- <button type="button" class="btn btn-primary" data-dismiss="modal" onclick="confirm_delete()">
- 确定
- button>
- div>
- div>
- div>
- div>
- <script>
- // 删除文章的函数
- function confirm_delete() {
- location.href = '{% url "delete" article.id %}'
- }
- script>

这里我们实际上实现了两个功能。
confirm_delete()删除文章。confirm_delete(),会调用Django的删除文章url:delete,实现删除功能。可能你认为删除文章功能实现起来没什么难度,但是请注意,上面的方法是有隐患的。要继续深入探讨,就得提到跨域请求伪造攻击,也称为CSRF攻击了(Cross-site request forgery)。
CSRF攻击你可以理解为:攻击者盗用了你的身份,以你的名义发送恶意请求。还是拿删除文章举例:
由于浏览器的同源策略,CSRF攻击者并不能得到你的登录数据实际内容,但是可以欺骗浏览器,让恶意请求附上正确的登录数据。不要小看CSRF攻击的威力:倘若是你的银行账户具有此类安全漏洞,黑客就可以神不知鬼不觉转走你的所有存款。
所以这里如何防范CSRF攻击的风险呢?方法是有的,即删除文章时用POST方法,并且校验csrf令牌。
前面我们讲到在 Django 中提交表单必须加csrf_token,这个就是CSRF令牌了,它防范CSRF攻击的流程如下:
由于攻击者并不能得到用户的 cookie 内容(仅仅是靠浏览器转发),所以通常情况下是无法构造出正确的 csrf_token 的,从而防范了此类攻击。
在Django中实现发送Post请求可以通过Form中指定请求方法。
首先在删除文章按钮代码下面增加一个隐藏的Form表单:
- <a href="#" data-bs-toggle="modal" data-bs-target="#myModal">删除文章a>
-
- <form
- style="display:none;"
- id="safe_delete"
- action="{% url "delete" article.id %}"
- method="POST"
- >
- {% csrf_token %}
- <button type="submit">发送button>
- form>
接着我们修改下我们的confirm_delete()函数。
- <script>
- // 删除文章的函数
- function confirm_delete() {
- document.getElementById("safe_delete").submit();
- }
- script>
最后我们改造下视图函数:
- # 删文章
- def article_delete(request, id):
- print(request.method)
- if request.method == 'POST':
- # 根据 id 获取需要删除的文章
- article = Article.objects.get(id=id)
- # 调用.delete()方法删除文章
- article.delete()
- return redirect("list")
- else:
- return HttpResponse("仅允许post请求")
之前我们的函数是直接调URL请求Django的视图删除函数。
现在我们改造之后,删除文章的代码流程如下:
csrf 校验),从而避免了 csrf 攻击。本篇我们学习了如何实现删除文章的功能,同时使用了Bootstrap的模态框弹框,来进行删除文章的二次校验。
涉及到删除功能,为了增加数据安全性,我们简单介绍了Django的csrf 令牌保护机制,并通过使用隐藏表单,发送Post请求的方式,启用了Django的crsf检验机制。
删除功能实际上还有一个重要的校验:用户校验,我们还没涉及,后面在增加用户模块功能的时候我们再安排增加上。
下一篇将学习如何更新文章。