has_one で, field_for を使う, accepts_nested_attributes_for
railsのヘルパーメソッドであるform_withでDBに情報登録する際に、主たる保存先のテーブル以外のところにレコードを保存したい時に使うのが、fields_forメソッドである。
今回はUserのテーブルに個人情報を登録する際、住所も入力させる。その際、住所はAddressテーブルという別の場所を指定する。
さてUserはそれぞれ住所を一個しか持っていないのでアソシエーションの関係は、
User modelでは has_one :address
Address modelでは belongs_to :user
となっている。この状況でのfields_forの使いかたをまとめる。
※HTMLはhamlで書いています
① Userモデルの記述
accepts_nested_attributes_for :address この記述によってUserモデルで一緒にAddressテーブルに保存ができるようにParamsを構成してくれるようになる。
この :address はhas_one :address でアソシエーション組んだモデル名と同じにする必要がある。これが異なると
No association found for name `address'. Has it been defined yet?
と怒られる。
② Viewファイルの記述
f.fields_for :address この引数でUserモデルの has_one :address を読み込んで自動で保存のためのパスを生成してくれている。
そしてaccepts_nested_attributes_for :addressが働いて、Paramsの中に address_attributes というハッシュを作ってくれる。この中にfields_for で入力した値を入れてパラメータとして送るのである。
ここを勘違いして引数を :addresses とかにしちゃうとアソシエーションを無視ししてしまい、accepts_nested_attributes_for :addressが働いてくれない。Paramsのなかに adresses: というハッシュができてその中に住所情報が格納される。でもこんなハッシュ名は保存先がないからエラーになってしまう。
(ちなみに第二引数の@user.address || Address.new はeditように既存レコードがあるかの判別をしている)
③ Controllerの記述
この情報登録で送られるパラメータはこうなる
Parameters: {"utf8"=>"✓", "authenticity_token"=>"toRFpCGlh+vdfjSkGB69zwkYFL9UkeRw0n",
"user"=>{"second_name"=>"保毛", "first_name"=>"帆桁",
"address_attributes"=>{"post_code"=>"111-2233"},
"commit"=>"登録する", "id"=>"1"}
ストロングパラメータでpermitを与えるキーにaddress_attributes: を加えてあげる。これでRailsが自動でアソシエーションを組んでいるAddressテーブルにレコードを送ってくれる。
●まとめ
名称を揃えなければいけないのは3箇所。
⑴ accepts_nested_attributes_for :address
⑵ has_one :address
⑶ f.fields_for :address
これがアソシエーションの関連がhas_many になったら複数形で合わせる。ここをしっかり抑えれば大丈夫。
<参考>
● https://ruby-rails.hatenadiary.com/entry/20141208/1418018874
● https://blog.dakatsuka.jp/2011/09/30/nested-has-one.html