Django搭建我的博客:編寫刪除文章功能

既然有了寫文章的功能,那固然也必需要有刪除文章的功能了。html

不安全的方式

有了以前的學習作鋪墊,刪除文章實現起來就比較簡單了。前端

首先增長一個視圖函數:python

article/views.py

...

# 刪文章
def article_delete(request, id):
    # 根據 id 獲取須要刪除的文章
    article = ArticlePost.objects.get(id=id)
    # 調用.delete()方法刪除文章
    article.delete()
    # 完成刪除後返回文章列表
    return redirect("article:article_list")
  • 與查詢文章相似,由於須要知道具體應該刪除哪一篇文章,所以必須傳入文章的id
  • 緊接着調用.delete()函數刪除數據庫中這篇文章的條目;
  • 刪除成功後返回到文章列表。

這裏與上一章同樣,不對用戶的身份進行限制,即任何人均可以刪除任意文章。固然這樣確定是不符合常理的,等到咱們學習了用戶管理的知識後,再回頭來修改。jquery

而後寫入路由信息:git

article/urls.py

...

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

這裏幾乎與文章詳情的寫法同樣,沒有新的內容。再次注意文章的id是如何傳遞到視圖中的。github

最後咱們但願可以在文章詳情的頁面進行刪除的操做(固然也能夠在專門的管理文章的頁面中),所以修改模板detail.htmlweb

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傳遞進去。數據庫

運行開發服務器,能夠發現已經可以正常的刪除文章了:django

增長彈窗

功能已經實現了,可是還有個小問題沒有解決:萬一我只是不當心點到了連接,辛辛苦苦寫的文章就被刪除了,豈不是欲哭無淚了?bootstrap

很容易想到的一個解決方法,就是點擊刪除按鈕是出現一個彈窗,確認後再進行刪除,確保用戶不是誤操做的。

彈窗是很經常使用的功能,可是想寫出一個美觀好用的彈窗卻不容易。幸運的是咱們不須要重複造輪子,早就有革命先驅作好相關的功能了,這裏咱們選擇使用Layer彈窗組件

layer是一款備受青睞的web彈層組件,具有全方位的解決方案。首先到官網下載Layer插件:Layer

解壓後將裏面的layer文件夾(含有layer.js的)直接複製到項目的static文件夾下。

爲了將來在全部頁面都能使用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。(固然Layer組件遠不止這些用法,具體可在官方文檔中查閱)。
  • 經過onclick實現了功能邏輯,所以href連接就不須要再跳轉了。
不要將模板語法和JavaScript語法搞混了。

包裹在 {% .. %} 中的是Django的模板語法。Django在模板語法幫助下將零散的模板文件拼接成完整的html文件,再傳遞到用戶的瀏覽器中進行解析。模板語法適合執行一些簡單的邏輯。

包裹在script標籤中的是JavaScript語法,它是與Python不一樣的、可以在瀏覽器中運行的前端語言。學好JavaScript一樣是一個漫長的過程,好在本教程中只會涉及一點基本的JavaScript代碼。

保存全部文件後刷新頁面,很好,達到了理想的效果:

安全的方式

可能你認爲刪除文章功能實現起來沒什麼難度,可是請注意,上面的方法是有隱患的。要繼續深刻探討,就得提到跨域請求僞造攻擊,也稱爲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 的,從而防範了此類攻擊。原理就是這樣,下面來看看如何實現安全的刪除功能。

代碼實現

首先修改刪除文章的連接,以及點擊它時調用的函數:

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

可能你要問了,沒發現哪行代碼校驗了csrf令牌啊?放心,默認配置下全部的 POST 請求都由 Django 中間件幫你驗證了。另外視圖必定要限制爲 POST 請求,即if request.method == 'POST'必須有,就請讀者思考一下緣由吧。

總結

本章新增了刪除博客文章的功能,而且使用了彈窗組件優化了用戶體驗。

另外一個重要的知識點就是CSRF攻擊的防範。記住一條,凡是重要的數據操做,都應該考慮帶有 csrf 令牌的 POST 請求;或者更簡單的方法,數據查詢用 GET,數據更改用 POST。

注意這個刪除文章的功能並無對用戶身份進行驗證。別急,等到講完用戶管理以後再來處理。

下一章將學習如何更新文章,準備好繼續做戰吧。

相關文章
相關標籤/搜索