基于Django的Blog 10.删除文章

1.增加一个视图方法

article/views.py

...

# 删文章
def article_delete(request, id):
    # 根据 id 获取需要删除的文章
    article = ArticlePost.objects.get(id=id)
    # 调用.delete()方法删除文章
    article.delete()
    # 完成删除后返回文章列表
    return redirect("article:article_list")

暂时谁都可以删文章,之后再改

2.写入路由信息

article/urls.py

...

urlpatterns = [
    ...
    # 删除文章
    path('article-delete/<int:id>/', views.article_delete, name='article_delete'),
]

这里的id是如何传递的呢?

猜想: 请求删除的时候,通过object方法获取对应文章id,把id传给url

3. 模板修改 增加delete链接

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>

...

4.增加弹窗

4.1首先到官网下载Layer插件:Layer

4.2 解压后将里面的layer文件夹(含有layer.js的)直接复制到项目的static文件夹下

4.3 在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。(当然Layer组件远不止这些用法,具体可在官方文档中查阅)。
  • 通过onclick实现了功能逻辑,因此href链接就不需要再跳转了

5.避免CSRF攻击

5.1CSRF令牌

CSRF令牌

前面我们讲到在 Django 中提交表单必须加csrf_token,这个就是CSRF令牌了,它防范CSRF攻击的流程如下:

  • 当用户访问 django 站点时,django 反馈给用户的表单中有一个隐含字段 csrf_token,这个值是在服务器端随机生成的,每次都不一样;
  • 在后端处理 POST 请求前,django 会校验请求的 cookie 里的 csrf_token 和表单里的 csrf_token 是否一致。一致则请求合法,否则这个请求可能是来自于 CSRF攻击,返回 403 服务器禁止访问。

由于攻击者并不能得到用户的 cookie 内容(仅仅是靠浏览器转发),所以通常情况下是无法构造出正确的 csrf_token 的,从而防范了此类攻击。原理就是这样,下面来看看如何实现安全的删除功能。

5.2 添加表单发起POST,并携带csrf令牌

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:

5.3 添加表单提交的url

article/urls.py

...
urlpatterns = [
    ...
    # 安全删除文章
    path(
        'article-safe-delete/<int:id>/',
        views.article_safe_delete,
        name='article_safe_delete'
    ),
]

5.4 视图部分修改

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请求")

可能你要问了,没发现哪行代码校验了csrf令牌啊?放心,默认配置下所有的 POST 请求都由 Django 中间件帮你验证了。另外视图一定要限制为 POST 请求,即if request.method == 'POST'必须有,就请读者思考一下原因吧。

posted on 2020-07-05 21:04  sunnywillow  阅读(72)  评论(0)    收藏  举报