accepts_nested_attributes_forの:allow_destroyオプション
accepts_nested_attributes_forメソッドはRails2.3からの新機能、nested_formを実現するためにactiverecordに追加されたメソッドです。
例えば、
class Member < ActiveRecord::Base has_many :posts accepts_nested_attributes_for :posts end
というモデルがあった場合に、アクションに
params = { :member => { :name => 'joe', :posts_attributes => { '0' =>{ :title => 'Kari, the awesome ruby documentation browser!' }, '1' => { :title => 'The egalitarian assumption of the modern citizen' } } }}
っていうようなパラメータが渡って来たときに、
member = Member.create(params['member'])
ってやると、
member.posts.length # => 2 member.posts.count # => 2 member.posts.first.title # => 'Kari, the awesome ruby documentation browser!' member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
って感じになります。
こりゃ便利ー!って訳で早速使い出しましたが、問題は更新時。これって該当のpostsを全部deleteして、insertし直したりしないよね?って不安になりましたが、こんな感じのパラメータ
params = { :member => { :name => 'joe', :posts_attributes => { '0' =>{ :title => 'Kari, the awesome Ruby documentation browser!' }, '1' => { :title => 'The egalitarian assumption of the modern citizen' } } }}
っていうのを渡して、例えばupdateアクションで
member = Member.find(params[:id]) member.update_attributes(params['member'])
ってやると、
member.posts.length # => 2 member.posts.count # => 2 member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!' member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
と更新されて、postsはちゃんとupdateされます。
んで、更に気になるのは、削除はどうすんのよ?ってこと。答えはちょー簡単でした。モデルを変更して、accepts_nested_attributes_forに :allow_destroy => trueのオプションを指定して、_deleteというパラメータが渡ってくるようにビューを変更するだけ!なので、
class Member < ActiveRecord::Base has_many :posts accepts_nested_attributes_for :posts, :allow_destroy => true end
と変更してあげて、
params = { :member => { :name => 'joe', :posts_attributes => { '0' => {:id => '1', :title => 'Kari, the awesome Ruby documentation browser!' }, '1' => {:id => '2', :title => 'The egalitarian assumption of the modern citizen', :_delete => '1' } } }}
っていう風にパラメータが来るようにビューを変更してあげれば、
member = Member.find(params[:id]) member.update_attributes(params['member'])
ってやると、
member.posts.length # => 2 member.posts.count # => 1 member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!' member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
気をつけないといけないのは、update_attributes直後のココ。
member.posts.length # => 2 member.posts.count # => 1
オブジェクトを保持している数を表すlengthは2だけど、DB上の数を表すcountは1になっているということ。
それから、ちょっと気になるのがバリデーション。素晴らしいのは、ちゃんと関係するオブジェクト全てのバリデーションがパスしないとDBへの反映がされないっちゅうことですね。
なので、accepts_nested_attributes_forで指定した関連の属性を送る場合は、単独のオブジェクトの場合と違って、属性を設定された関連するオブジェクト群のバリデーションが実行されて、それぞれの登録・更新・削除が実行されます。
上の例では、has_manyの関連で説明しましたが、has_one/belongs_to/has_and_belongs_to_manyの関連でも使用可能です。すばらしー。
これは複雑なオブジェクトを操作する際には欠かせませんね!