
※ 20140307 一部追記修正しました
new/create時に関連テーブルも更新する記述について、忘れていたのでまとめてみました。
(意外と参考書とかにも載っておらず・・・)
今回利用しているバージョンは下記。

こんなイメージです。postを1件更新するときに関連テーブル(authors)と中間テーブル(author_posts)を複数件数一気に更新する感じです。
# app/models/post.rb class Post < ActiveRecord::Base has_many :author_posts has_many :authors, :through => :author_posts accepts_nested_attributes_for :authors end
# app/models/author.rb class Author < ActiveRecord::Base has_many :author_posts has_many :posts, :through => :author_posts end
# app/models/author_post.rb class AuthorPost < ActiveRecord::Base belongs_to :post belongs_to :author end
# postのインスタンスを作成
irb > post = Post.new
=> #<Post id: nil, title: nil, created_at: nil, updated_at: nil>
# 空のインスタンスを確認
irb > post
=> #<Post id: nil, title: nil, created_at: nil, updated_at: nil>
# post.author_postsの確認
# 空のArray
irb > post.author_posts
=> #<ActiveRecord::Associations::CollectionProxy []>
# post.authorsの確認
# 空のArray
irb > post.authors
=> #<ActiveRecord::Associations::CollectionProxy []>
# buildで、postの関連テーブルauthorのインスタンスを作成
irb > post.authors.build
=> #<Author id: nil, name: nil, created_at: nil, updated_at: nil>
# post.authorsのArrayに、作成したインスタンスが格納されている
irb > post.authors
=> #<ActiveRecord::Associations::CollectionProxy [#<Author id: nil, name: nil, created_at: nil, updated_at: nil>]>
# 試しにもう一回buildしてみる
irb > post.authors.build
=> #<Author id: nil, name: nil, created_at: nil, updated_at: nil>
# post.authorsのArrayに、2個目のインスタンスがpushされている
irb > post.author
=> #<ActiveRecord::Associations::CollectionProxy [#<Author id: nil, name: nil, created_at: nil, updated_at: nil>, #<Author id: nil, name: nil, created_at: nil, updated_at: nil>]>
# post.author_posts のArrayはこの時点では空
irb > post.author_posts
=> #<ActiveRecord::Associations::CollectionProxy []>
# saveしてみる
# 関連テーブルにもINSERTされている
irb > post.save
(0.5ms) BEGIN
SQL (4.4ms) INSERT INTO `posts` (`created_at`, `updated_at`) VALUES ('2014-02-23 08:20:58', '2014-02-23 08:20:58')
SQL (0.2ms) INSERT INTO `authors` (`created_at`, `updated_at`) VALUES ('2014-02-23 08:20:58', '2014-02-23 08:20:58')
SQL (0.3ms) INSERT INTO `author_posts` (`author_id`, `created_at`, `post_id`, `updated_at`) VALUES (3, '2014-02-23 08:20:58', 10, '2014-02-23 08:20:58')
SQL (0.2ms) INSERT INTO `authors` (`created_at`, `updated_at`) VALUES ('2014-02-23 08:20:58', '2014-02-23 08:20:58')
SQL (0.2ms) INSERT INTO `author_posts` (`author_id`, `created_at`, `post_id`, `updated_at`) VALUES (4, '2014-02-23 08:20:58', 10, '2014-02-23 08:20:58')
(2.2ms) COMMIT
=> true
# save後に post.author_posts を再確認
# Arrayに値が入っている
irb > post.author_posts
=> #<ActiveRecord::Associations::CollectionProxy [#<Author id: 1, name: nil, created_at: "2014-02-23 08:41:41", updated_at: "2014-02-23 08:41:41">, #<Author id: 2, name: nil, created_at: "2014-02-23 08:41:41", updated_at: "2014-02-23 08:41:41">]>
関連オブジェクトの値を更新する際は、post.authors.build のようにする必要があります。
複数個の場合は複数回buildしてあげればよさそうです。
上記を元に、View、Controllerを作っていきます。
# app/views/posts/_form.html.erb <%= form_for(@post) do |f| %> <% @post.authors.each do |author| %> <%= f.fields_for :authors, author do |author_field| %> <%= author_field.text_field :name %> <% end %> <% end %> <% end %>
“authors_attributes[]”の部分は、普通は:authorsと書くようですが・・・
複数個更新するときには上記のように書くと、同名のパラメータを配列に格納してくれます。
また、authorsのnameカラムを更新したいので、text_fieldには :name と記述します。
追記):authors でも動作しました。
ポストされるパラメータ
params[:post][:authors_attributes]の中身は下記になります。
"authors_attributes"=>[{"name"=>"著者1"}, {"name"=>"著者2"}, {"name"=>"著者3"}]}
(実験)f.fields_forの引数を :authors にした場合のパラメータ
"authors_attributes"=>{"0"=>{"name"=>"あ"}, "1"=>{"name"=>"い"}, "2"=>{"name"=>"う"}}}
keyに個数を表す数値が入ってきてしまいました。
これが気持ち悪くて配列で取得するようにしています。
追記)ハッシュ形式の方が正しいようです。
# app/controllers/posts_controller.rb
def new
@post = Post.new
# 今回は分かりやすく、authorは固定で3枠作成
# 1postで最大3author追加出来る
3.times {
# 関連オブジェクトをbuild
@post.authors.build
}
end
def create
@post = Post.new(post_params);
@post.save
end
# strong parameters
private
def post_params
params.require(:post).permit(:title, authors_attributes: [:name])
end
本当はcreateに何も追記したくなかったのですが・・・(これ以外の方法がわかりませんでした。)
また、strong parametersのホワイトリストに :authors_attributes => [] を追記しています。
追記)上記のようにstrong parametersに記述をすることで、余計な処理を書かずに更新出来ました。
ただし、データを取り出して何らかの加工をする場合には、Arrayで取得しても良さそうです。