accepts_nested_attributes_for
を使って1つのフォームで複数のテーブルを同時に更新している場合、子となるモデル側の更新処理をカスタマイズしたいケースがあります。
例えば画像を投稿するフォームで永続化しないパラメータとして photo
のような属性を定義している場合、「画像のみを変更する場合」は AR::Base#changed?
が true にならず、before_save
などのコールバックを含め更新処理が一切走らず困ったことになります。
Bad Case
class FavoriteIdol < ActiveRecord::Base
attr_accessor :photo
before_save :save_photo
private
def save_photo
if photo
self.photo_saved_at = Time.now
upload_photo(photo)
end
end
end
Solution
accepts_nested_attributes_for :relation
を呼びだした際に定義される relation_attributes=
というメソッドをオーバーライドします。
引数にはポストされたparamsの配列が格納されています。この配列を使い、関連オブジェクトの値を書き換える事で任意の更新処理を実装することができます。
例
class User < ActiveRecord::Base
accepts_nested_attributes_for :favorite_idols
has_many :favorite_idols
def favorite_idols_attributes=(listed_attributes)
listed_attributes.each do |index, attributes|
idol = favorite_idols.detect{|i| i.id == attributes["id"].to_i } || favorite_idols.build
idol.assign_attributes(attributes)
idol.photo_saved_at = Time.now if idol.photo.present?
end
end
end