kakurenbo_putiを使いつつaccepts_nested_attributes_for :hoges, allow_destroy: trueしたい場合


論理削除でも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カラム)に現在時刻を設定してアサインしておきました。

これで、ネストされたモデルであっても論理削除が可能となりました。