Rails 2.3 で acts_as_paranoid を使ってみた
Railsで論理削除を行う方法の1つとして、acts_as_paranoidというプラグインがあるので、Rails2.3で試してみた。なお、rails3用にはrails3_acts_as_paranoidがあるようです。
acts_as_paranoidとは?
データベースのテーブルのカラムに deleted_at を作って、deleted_atに日付があれば論理削除された状態、deleted_at が null なら削除されていない状態というのを管理してくれるプラグイン。
install
$ cd RAILS_ROOT
$ ruby script/plugin install git://github.com/technoweenie/acts_as_paranoid.git
データベースを作成
migrationを作成して、カラム名 deleted_at、属性 datetime を持つテーブルを作成します。
- ./db/migrate/001_create_table_hoges.rb
class CreateTableHoges < ActiveRecord::Migration def self.up create_table(:hoges) do |t| t.column :name, :string, :null => false t.column :deleted_at, :datetime end end def self.down drop_table :hoges end end
論理削除されるか実験
$ ruby script/console Hoge.new(:name => "fuga").save true Hoge.find(:all) [ [0] #<Hoge:0xb75546ac> { :id => 1, :name => "fuga", :deleted_at => nil } ] Hoge.destroy_all [ [0] #<Hoge:0xb755091c> { :id => 1, :name => "fuga", :deleted_at => Fri Sep 02 19:40:30 +0900 2011 } ] Hoge.find(:all) [] Hoge.find_with_deleted(:all) [ [0] #<Hoge:0xb753e820> { :id => 1, :name => "fuga", :deleted_at => Fri Sep 02 19:40:30 +0900 2011 } ]
その他の使い方
こちらを見ると分かります。
# Overrides some basic methods for the current model so that calling #destroy sets a 'deleted_at' field to the current timestamp. # This assumes the table has a deleted_at date/time field. Most normal model operations will work, but there will be some oddities. # # class Widget < ActiveRecord::Base # acts_as_paranoid # end # # Widget.find(:all) # # SELECT * FROM widgets WHERE widgets.deleted_at IS NULL # # Widget.find(:first, :conditions => ['title = ?', 'test'], :order => 'title') # # SELECT * FROM widgets WHERE widgets.deleted_at IS NULL AND title = 'test' ORDER BY title LIMIT 1 # # Widget.find_with_deleted(:all) # # SELECT * FROM widgets # # Widget.find_only_deleted(:all) # # SELECT * FROM widgets WHERE widgets.deleted_at IS NOT NULL # # Widget.find_with_deleted(1).deleted? # # Returns true if the record was previously destroyed, false if not # # Widget.count # # SELECT COUNT(*) FROM widgets WHERE widgets.deleted_at IS NULL # # Widget.count ['title = ?', 'test'] # # SELECT COUNT(*) FROM widgets WHERE widgets.deleted_at IS NULL AND title = 'test' # # Widget.count_with_deleted # # SELECT COUNT(*) FROM widgets # # Widget.count_only_deleted # # SELECT COUNT(*) FROM widgets WHERE widgets.deleted_at IS NOT NULL # # Widget.delete_all # # UPDATE widgets SET deleted_at = '2005-09-17 17:46:36' # # Widget.delete_all! # # DELETE FROM widgets # # @widget.destroy # # UPDATE widgets SET deleted_at = '2005-09-17 17:46:36' WHERE id = 1 # # @widget.destroy! # # DELETE FROM widgets WHERE id = 1 #
関連モデルとの関係
関連モデルにも宣言してあれば、一緒に論理削除してくれます。参考
注意点
残念な事に、acts_as_paranoidはsave(createやupdate)までは面倒を見てくれません。save時には全件を対象に一意の調査してしまうのです。
これを回避するには:scopeを指定してしまうのが良いでしょう。