利用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