利用 ActiveRecord 提供的 dependent 選項處理資料相依性

最近在 ptt 的 Ruby 板,有板友在討論刪除資料時如何進行驗證,原 po 的問題大致上是說倘若某一筆資料跟其他的資料有所關聯,該如何顯示錯誤訊息給使用者、告知因為有資料的關聯參照存在所以無法刪除。

看到這個問題,我心中第一個想到的是「就用 CASCADE 的方式把資料一口氣刪光啊!」XD 不過隨即想想,大多數的資料可不是說想刪就能隨便刪的,例如在 Ruby 板上的討論串就有人提到要刪除某個分類名稱,但在刪除前要先將該分類底下的文章都指定為未分類。

總之,好奇心驅使之下,我猜想 ActiveRecord 裡面一定有針對關聯性特別設計一套機制來處理,果不其然其實在 has_one, has_many 底下就有: dependent 來處理互相參照的資料。

:dependent 有三個選項:(在此假設 User has_many Posts, Post has_many Comments)

class User < ActiveRecord::Base
  has_many :posts, :dependent => :destroy
end
  • :destroy:呼叫 User.destroy 時,ActiveRecord 會呼叫 Post.destroy 來達到刪除 Posts 的目的,也就是與該 Posts 相關聯的 comments 也會被一併刪除。(除非 Post 與 Comments 之間的關係是 nullify)
  • :delete_all:呼叫 User.destroy 時,僅刪除 User 本身及該 user 的 posts。
  • :nullify:不刪除關聯的物件,僅將 User 的 posts 之 Foreign Key 改為 NULL

至於要在物件刪除之前,若要做驗證動作,就如同 ptt Ruby 的板主 godfat 分享的一樣,使用 before_destroy 可以在呼叫 destroy 之前先做其他動作。例如在 這個網頁 裡面看到的 sample code(before_destroy 是寫在 Model 裡,destroy 的動作則是由 controller 中的 action 來呼叫):

class Group < ActiveRecord::Base
  acts_as_tree :order => "name"

  has_and_belongs_to_many => :users

  before_destroy :validates_no_dependents

  def validates_no_dependents
    if children.size > 0 || users.size > 0
      errors.add :base, "Cannot delete this group, as it has sub-groups and/or users"
    end
  end
end