前文我们实现了分别实现了显示文章,创建文章等功能。

Python + Django4 搭建个人博客(十一): 利用表单实现创建文章的功能页面_李威威wiwi的博客-CSDN博客本篇我们学习了使用Django用于处理表单的Form类,利用forms.ModelForm 类的继承功能,我们实现了表单和模型的直接关联。基于表单功能,我们完成了创建文章的功能页面。接下来两篇我们陆续完成剩下的文章修改和删除的功能页面。...Python + Django4 搭建个人博客(十二):实现删除文章功能页面-LMLPHPhttps://blog.csdn.net/agelee/article/details/126644558

本篇我们实现删除文章的功能。

简单功能实现

删除文章单从功能实现上面来说很简单,视图函数如下:

# 删文章
def article_delete(request, id):
    # 根据 id 获取需要删除的文章
    article = Article.objects.get(id=id)
    # 调用.delete()方法删除文章
    article.delete()
    # 完成删除后返回文章列表
    return redirect("list")
  • 与查询文章类似,因为需要知道具体应该删除哪一篇文章,因此必须传入文章的id;
  • 紧接着调用.delete()函数删除数据库中这篇文章的条目;
  • 删除成功后返回到文章列表。

然后修改下路由配置文档

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/<int:id>/', views.article_detail, name='detail'),  # 文章详情
    path('create/', views.article_create, name='create'),  # 写文章
    # 增加生删除文章
    path('delete/<int:id>/', views.article_delete, name='delete'),
]

最后修改模板,在详情页面detail.html增加一个删除文章的按钮。

<!-- 文章详情 -->
    <div class="container">
        <!--    <div class="row">-->
        <!-- 标题及作者 -->
        <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>
                &nbsp
                <a>{{ article.created|date:'Y-m-d H:i:s' }}</a>
                &nbsp
                <a href="{% url "delete" article.id %}">删除文章</a>   #增加此项
            </div>
        </div>
        <!-- 文章正文 -->
        <div class="col-12">
            <p>{{ article.body }}</p>
        </div>
    </div>

运行服务器之后,可以看到已经增加了删除文章按钮。

Python + Django4 搭建个人博客(十二):实现删除文章功能页面-LMLPHP

删除弹窗确认功能

上面实现了一个简单的功能,不过有一个隐患,就是如果是不小心点到了删除按钮,可能连后悔的机会都没了。

一般这种涉及到数据直接清除的动作,要加一个弹窗确认的动作,二次确认后才能直接删除数据库数据。

实现这个功能我们可以使用Bootstrap的模态框的组件。

模态框(Modal)是覆盖在父窗体上的子窗体。通常,目的是显示来自一个单独的源的内容,可以在不离开父窗体的情况下有一些互动。子窗体可提供信息交互等。

detail.html中之前新增加的那行删除文章代码进行如下修改:

 <a href="#" data-bs-toggle="modal" data-bs-target="#myModal">删除文章</a>

增加了模态框效果,点击此链接会跳弹出名为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>

Python + Django4 搭建个人博客(十二):实现删除文章功能页面-LMLPHP

这里我们实际上实现了两个功能。

  • 实现了名为myModal的模态框,在模态框中增加了两个按钮:取消,确认。点击确认会调用JS函数confirm_delete()删除文章。
  • 编写了函数confirm_delete(),会调用Django的删除文章url:delete,实现删除功能。

安全的方式

可能你认为删除文章功能实现起来没什么难度,但是请注意,上面的方法是有隐患的。要继续深入探讨,就得提到跨域请求伪造攻击,也称为CSRF攻击了(Cross-site request forgery)。

CSRF攻击

CSRF攻击你可以理解为:攻击者盗用了你的身份,以你的名义发送恶意请求。还是拿删除文章举例:

  • 用户登录了博客网站A,浏览器记录下这次会话,并保持了登录状态;
  • 用户在没有退出登录的情况下,又非常不小心的打开了邪恶的攻击网站B
  • 攻击网站B在页面中植入恶意代码,悄无声息的向博客网站A发送删除文章的请求,此时浏览器误以为是用户在操作,从而顺利的执行了删除操作。

由于浏览器的同源策略,CSRF攻击者并不能得到你的登录数据实际内容,但是可以欺骗浏览器,让恶意请求附上正确的登录数据。不要小看CSRF攻击的威力:倘若是你的银行账户具有此类安全漏洞,黑客就可以神不知鬼不觉转走你的所有存款。

所以这里如何防范CSRF攻击的风险呢?方法是有的,即删除文章时用POST方法,并且校验csrf令牌。

CSRF令牌

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

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

由于攻击者并不能得到用户的 cookie 内容(仅仅是靠浏览器转发),所以通常情况下是无法构造出正确的 csrf_token 的,从而防范了此类攻击。

利用Form发送Post请求

在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的视图删除函数。

现在我们改造之后,删除文章的代码流程如下:

  • 点击删除文章链接时,弹出模态框
  • 点击模态框的确认按钮后,通过JS函数代码找到隐藏的Form,并提交表单。
  • 表单发起 POST 请求,并携带了 csrf 令牌(Django对所有的Post请求都通过中间件默认进行csrf 校验),从而避免了 csrf 攻击。

结语

本篇我们学习了如何实现删除文章的功能,同时使用了Bootstrap的模态框弹框,来进行删除文章的二次校验。

涉及到删除功能,为了增加数据安全性,我们简单介绍了Django的csrf 令牌保护机制,并通过使用隐藏表单,发送Post请求的方式,启用了Django的crsf检验机制。

删除功能实际上还有一个重要的校验:用户校验,我们还没涉及,后面在增加用户模块功能的时候我们再安排增加上。

下一篇将学习如何更新文章。

09-05 16:45