論理削除でもallow_destroy: trueしたい
データの持ちかたの関係上、論理削除が必要になったので、gem kakurenbo_putiを使って実装したのですが、そのモデルがfields_forを使って保存・削除するタイプのモデルだったもんで、どうしたものかと小一時間悩みました。
Hogeモデル
class Hoge < ActiveRecord::Base
belongs_to :foo
soft_deletable column: :deleted_at
end
Fooモデル(before)
class Foo < ActiveRecord::Base
has_many :hoges, dependent: :destroy, inverse_of: :foo
# allow_destroy: true だと物理削除されてしまう件
accepts_nested_attributes_for :hoges, allow_destroy: true
end
accepts_nested_attributes_forの更新処理をカスタマイズ
調べていたら、accepts_nested_attributes_forの更新処理をカスタマイズするという投稿を見つけました。
これを使えばできそうです。
Fooモデルを改修する
Fooモデル(after)
class Foo < ActiveRecord::Base
has_many :hoges, dependent: :destroy, inverse_of: :foo
# allow_destroy: trueを残すと事故で消されかねないので消しておく
accepts_nested_attributes_for :hoges
# accepts_nested_attributes_forの更新処理をカスタマイズ
# @see http://qiita.com/tkyowa/items/e751dcf8958b8c86b686
def hoges_attributes=(listed_attributes)
listed_attributes.each do |index, attributes|
hoge = hoges.detect { |hoge|
hoge.id == attributes['id'].to_i
} || hoges.build
_destroy = attributes.delete("_destroy")
if _destroy
attributes["deleted_at"] = Time.zone.now
end
hoge.assign_attributes(attributes)
end
end
end
解説します。
allow_destroy: true
を設定している場合、_destroy
という属性が一緒に渡ってきていてそれを反映させると、物理的に削除されるので、attributes.delete("_destroy")
でその属性を削除しています(値は_destroy
という変数にとっておく)。
属性がない場合にdeleteメソッドが実行された場合は_destroy
の値はnilなので何もしませんが、ある場合はkakurenbo_putiの論理削除用に作ったカラム(ここではdeleted_at
カラム)に現在時刻を設定してアサインしておきました。
これで、ネストされたモデルであっても論理削除が可能となりました。