在 Django 中實現軟刪除功能:完整指南

更新日期: 2024 年 12 月 3 日

在應用中,某些資料可能需要保留歷史記錄而不是直接刪除,例如留言系統中的留言記錄。

透過「軟刪除」,我們可以標記資料為已刪除,而不是實際從資料庫中刪除。

以下將詳細說明如何實現軟刪除功能。


軟刪除的設計概念

軟刪除的需求

  • 避免資料丟失
    軟刪除不會真正刪除資料,而是通過標記的方式(如設置 deleted_at 時間戳)判定資料是否有效。
  • 提升操作靈活性
    支援資料的恢復和追蹤已刪除資料的歷史記錄。

使用 deleted_at 欄位

  • 新增一個 deleted_at 欄位作為軟刪除的標記:
    • null=True:允許未刪除的資料此欄位為空。
    • 使用 DateTimeField 紀錄刪除時間,若資料未刪除,該欄位為 NULL
  • 優化查詢速度:使用索引
    deleted_at 欄位上設置索引(db_index=True),可以提升查詢效率。
    • 注意:雖然索引加快了讀取速度,但可能影響新增和更新的效能,因為需要維護索引。

修改模型以支持軟刪除

comments/models.py 中,對 Comment 模型進行調整:

from django.db import models
from resumes.models import Resume

class Comment(models.Model):
    resume = models.ForeignKey(Resume, on_delete=models.CASCADE)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    deleted_at = models.DateTimeField(null=True, db_index=True)  # 軟刪除欄位

調整刪除邏輯

代碼所在目錄位置

mysite/
├── comments/
   ├── views.py  # 刪除邏輯的修改

comments/views.py 中新增一個刪除函數,將邏輯改為設置 deleted_at 欄位的值,而非真正刪除資料。

代碼說明

from django.shortcuts import get_object_or_404, redirect
from django.views.decorators.http import require_POST
from django.utils import timezone
from .models import Comment

@require_POST
def delete(request, id):
    # 獲取指定的 Comment
    comment = get_object_or_404(Comment, id=id)
    # 設置刪除時間
    comment.deleted_at = timezone.now()
    comment.save()
    # 轉址回 Resume 詳細頁
    return redirect("resumes:show", id=comment.resume.id)

運作機制

  1. 獲取資料
    get_object_or_404(Comment, id=id) 根據 ID 從資料庫查詢 Comment。若未找到,返回 404 頁面。
  2. 標記為已刪除
    使用 timezone.now() 設定 deleted_at 欄位為當前時間,作為刪除標記。
  3. 保存修改
    調用 .save() 更新資料庫。
  4. 轉址到詳情頁
    調用 redirect,並使用 id=comment.resume.id 指定轉址目標的 ID。

深入解析:id=comment.resume.id 的運作機制

comment.resume.id 是什麼?

  • comment
    一個從資料庫中查詢出的 Comment 物件,包含與該留言相關的所有欄位資料。
  • comment.resume
    透過 ForeignKey 關聯模型,comment.resume 獲取與該留言相關聯的 Resume 物件。
  • comment.resume.id
    取得該 Resume 的主鍵 ID,作為 redirect 的參數傳遞,用於生成目標頁面的 URL。

運作流程示意圖

1. 瀏覽器發送 POST 請求至 /comments/<comment_id>/delete
2. 後端 `delete` 函數執行:
   - 從資料庫獲取 Comment 物件
   - 設置 `deleted_at` 時間戳記
   - 通過 `comment.resume.id` 獲取 Resume  ID
3. 轉址至 /resumes/<resume_id> 詳細頁

常見問題

為什麼可以直接使用 comment.resume

  • 因為在 Comment 模型中,我們使用了 ForeignKeyResume 進行了關聯:
resume = models.ForeignKey(Resume, on_delete=models.CASCADE) 
  • 這使得 Django 自動為 Comment 物件添加了一個屬性 resume,用於獲取相關聯的 Resume 物件。

修改顯示邏輯

代碼所在目錄位置

mysite/
├── resumes/
│   ├── views.py       # 調整顯示邏輯

修改 show 函數

resumes/views.py 中,過濾掉已刪除的留言:

def show(request, id):
    # 找到指定的 Resume
    resume = get_object_or_404(Resume, id=id)

    # 僅顯示未刪除的 Comment
    comments = resume.comment_set.order_by("-created_at").filter(deleted_at=None)

    return render(
        request,
        "resumes/show.html",
        {
            "resume": resume,
            "comments": comments
        }
    )

重點解析

  1. comment_set 的使用
    Django 自動為 ForeignKey 關聯模型創建了一個反向管理器,comment_set 用於訪問與 Resume 關聯的所有 Comment
  2. filter(deleted_at=None)
    過濾掉已刪除的留言,僅顯示 deleted_at 為空的資料。

    等號在這裡的語法是 Python 的關鍵字引數(keyword argument)語法。這與函數調用時的關鍵字參數使用方式一致。

    對於 filter,它內部會解讀這些關鍵字參數並將它們映射到資料庫的查詢條件。例如,deleted_at=None 翻譯成 SQL 語句時,會變成類似於 WHERE deleted_at IS NULL
  3. 排序顯示
    使用 .order_by("-created_at") 按照留言的建立時間倒序排列,保證最新留言顯示在最上方。

更新模板

resumes/templates/resumes/show.html 中,留言的顯示邏輯保持不變,仍然按時間排序顯示:

<ul>
  {% for comment in comments %}
  <li>
    {{ comment.content|linebreaksbr }}
    <small>{{ comment.created_at }}</small>
    <form action="{% url 'comments:delete' comment.id %}" method="post">
      {% csrf_token %}
      <button type="submit">刪除</button>
    </form>
  </li>
  {% endfor %}
</ul>

為什麼選擇時間戳而非布林值

時間戳的優勢

  • 追蹤歷史記錄
    可以記錄資料的刪除時間,便於日後進行數據分析或恢復。
  • 操作靈活性
    相較於布林值(is_deleted),時間戳允許多層次的操作,例如:
    • 判定刪除資料的存續時間。
    • 自動清理刪除超過一定時間的資料。

SQLite 不支持布林值

  • 雖然 SQLite 不直接支持布林值,但可以通過 BooleanField 模擬。然而,DateTimeField 的時間戳記錄更為精確,適合實現軟刪除。

小結

功能完成的成果

  1. 支持軟刪除邏輯:刪除資料時不會真正移除,而是標記為已刪除。
  2. 資料顯示邏輯調整:過濾已刪除的留言,保持介面的清晰性。
  3. 提升資料管理靈活性:通過 deleted_at 時間戳記錄刪除時間,支持多樣化的操作。

適用場景

  • 保留歷史資料:例如留言、交易記錄等。
  • 後續數據恢復:允許用戶手動或自動恢復刪除的資料。

Django 的軟刪除功能不僅能提升資料的安全性,還能滿足更高層次的業務需求,適合對數據管理有嚴格要求的應用場景。希望本指南能幫助你快速掌握軟刪除的實現方法!

Similar Posts

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *