ARで論理削除する為のモジュールを作ってみた.
Railsで論理削除が必要になったのでとりあえず調べてみた.希望としては
- プラグインに頼らず手軽に使いたい
- ARの機能そのままで使いたい
調べてみると以下の二つがでてきた.
acts_as_paranoidがググってみると結構多いが別に高機能を求めてる訳じゃないのでこれは使いたくない、っでRails作るときに参考にしているskipのソースを読むとモジュールにしているのがあった.
こっちは手軽で便利だけど論理削除を実装で意識する必要が強いのでこれを参考にオレオレモジュールを作ってみた.
論理削除オレオレモジュール
module Models # # 論理削除するようにします. # # 論理削除にはdeleted_atカラムを使用します.マイグレーションにdatetime型のカラムを追加してください. # 削除の判定にはdeleted_atがnullでない場合は削除済みとして扱われます. # # # find等のメソッドから削除されたレコードを取得しないようにする例 # class Member < ActiveRecord::Base # include Models::LogicalDestroyable # default_scope :conditions => {:deleted_at => nil} # end # # # default_scopeを指定する場合 # class Member < ActiveRecord::Base # include Models::LogicalDestroyable # hide_destroyed_with_default_scope # end # module LogicalDestroyable def self.included(base) base.alias_method_chain :destroy, :logical_destroyable base.named_scope(:active, {:conditions => {:deleted_at => nil}}) base.attr_protected :deleted_at end # # default_scopeを使って削除されたレコードを検索結果から隠します # # findメソッドなどで削除項目も対象にしたい場合はwith_exclusive_scopeを使ってください. # def self.hide_destroyed_with_default_scope # create時にも初期値として使われるけど大丈夫かな? self.default_scope :conditions => {:deleted_at => nil} #default_scopeは最後のdefault_scopingの値しか見ないので使えないっぽいからdefault_scopeを使うときは都度指定する必要がある. #options = {:conditions => {:deleted_at => nil}} #base.default_scoping.insert 0, { :find => options, :create => options[:conditions] } end def destroy_with_logical_destroyable before_destroy_with_logical_destroyable current_time = current_time_from_proper_timezone ret=update_attribute(:deleted_at, current_time) after_destroy_with_logical_destroyable ret end def before_destroy_with_logical_destroyable;end def after_destroy_with_logical_destroyable;end def destroyed? !self.deleted_at.nil? end alias_method :deleted?, :destroyed? def recover update_attribute(:deleted_at, nil) end end end
あとがき
とりあえずモジュール作ってみたけどdeleteとdestroyって似てるけど内部の処理が違うんだよなー.
- delete
- DELETE文(SQL)で消す
- destroy
- ARオブジェクトを一旦作ってから削除
- before_destroyコールバックで関連付けされてるやつも消したりするように成ってる.
関連づけ先が勝手に消されないようにbefore_destroyコールバックとは別のコールバックするようにしたけど.
deleteをオーバライドして関連まで影響ない仕様をそのまま論理削除に置き換えて作れば良かったかも.
物理削除の場合はdestroyするようにすればARの仕様を大きく変えないしなー.