Link Search Menu Expand Document

19. 軟刪除和版本控制

19-1 需求說明

在實際運作的網站中,用戶可能會不小心刪除資料,或是修改時不小心改錯等等。這時候用戶可能會透過客服希望管理員能進行復原的動作。另一方面,針對重要的資料,我們也希望建立追蹤和稽核的機制。

這種需求叫做軟刪除(Soft Deletion)和版本控管(Versions)。

軟刪除(Soft Deletion)的意思是不要真的刪除這一筆資料,常見的作法是增加一個刪除的標記字段(例如 deleted_at字段),如果被標記刪除了,那就不要顯示出來即可,使用 Paranoia 這個 gem 可以完成這個功能。

至於版本控管的作法,則會另外建立一個 Version Model 來存儲編修紀錄。如果本來的資料被刪除或修改,則會複製資料到這個 Model 去。等會我們示範用 paper_trail gem。如果用了 paper_trail 來做版本控管,也就不需要用 paranoia 了。因為前者也可以做刪除的復原。

不建議用 paranoia 的方式,這種方式有一個嚴重的資料庫問題,會干擾 unique index。例如 User 的 email 是唯一的,如果不真的刪除這一筆資料,只是標記被刪除的話,那麽被刪除的用戶資料,該 email 依然無法註冊。因為資料庫認為 email 重復了。

19-2 安裝使用 paper_trail

編輯 Gemfile

+  gem 'paper_trail'

執行 bundle,重啟伺服器

執行 bundle exec rails generate paper_trail:install --with-changes

執行 bundle exec rake db:migrate

編輯 app/models/registration.rb

  class Registration < ApplicationRecord

+   has_paper_trail

這樣就完成了,所有的修改和刪除,都會紀錄在 paper_trail 的 Version model 裡面。

paper_trail 文檔上,有完整的版本瀏覽、比較和復原的作法。

19-3 編修紀錄的 UI 和復原

我們可以在後台新增一個 UI 來瀏覽最近的編修紀錄:

編輯 config/routes.rb

  namespace :admin do
    root "events#index"

+   resources :versions do
+     post :undo
+   end

執行 rails g controller admin::versions

編輯 app/controllers/admin/versions_controller.rb

-  class Admin::VersionsController < ApplicationController
+  class Admin::VersionsController < AdminController

+    def index
+      @versions = PaperTrail::Version.order("id DESC").page(params[:page])
+    end

+    def undo
+      @version = PaperTrail::Version.find(params[:version_id])
+      @version.reify.save!
+
+      redirect_to admin_versions_path
+    end

   end

新增 app/views/admin/versions/index.html.erb

<h2>編修紀錄</h2>

<table class="table">
<tr>
  <td>ID</td>
  <td>Model</td>
  <td>Model ID</td>
  <td>事件</td>
  <td></td>
  <td>操作者</td>
  <td></td>
</tr>
<% @versions.each do |version| %>
  <tr>
    <td><%= version.id %></td>
    <td><%= version.item_type %></td>
    <td><%= version.item_id %></td>
    <td><%= version.event %></td>
    <td>
      <ul>
      <% version.changeset.each do |key, value| %>
        <li>從 <%= value[0] %> 改成 <%= value[1] %></li>
      <% end %>
      </ul>
    </td>
    <td><%= version.whodunnit && User.find(version.whodunnit).display_name %></td>
    <td>
      <% if version.event != 'create' %>
      <%= link_to "Undo", admin_version_undo_path(version), :data => { :confirm => "Are you sure?"}, :method => :post, :class => "btn btn-danger" %>
      <% end %>
    </td>
  </tr>
<% end %>
</table>

<%= paginate @versions %>

瀏覽 http://localhost:3000/admin/versions

你可以試著刪除或編輯報名資料看看,可以看到編修紀錄,並進行復原(Undo)。

image


Copyright © 2010-2021 Wen-Tien Chang All Rights Reserved.