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モデルの記述

 class User < ApplicationRecord

  has_one :address, dependent: :destroy
  accepts_nested_attributes_for :address

 end

 accepts_nested_attributes_for :address この記述によってUserモデルで一緒にAddressテーブルに保存ができるようにParamsを構成してくれるようになる。

 この :address はhas_one :address でアソシエーション組んだモデル名と同じにする必要がある。これが異なると

No association found for name `address'. Has it been defined yet?

 と怒られる。

 

 Viewファイルの記述

= form_with model: @user, local: true do |f|
.form-group
 = f.label "お名前"
 = f.text_field :second_name,class:"input-default"
 = f.text_field :first_name,class:"input-default"

 = f.fields_for :address, @user.address || Address.new do |address|
   .form-group
    = address.label "郵便番号"
 
     = address.text_field :post_code ,class:"input-default"
 

 

 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の記述


 def update
 
  @user.update(user_params)
  redirect_to root_path
 end

 private
 

 def user_params
  params.require(:user).permit(
  :second_name,:first_name,
  address_attributes: [:post_code]
   )
 end 

 この情報登録で送られるパラメータはこうなる

  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://railsguides.jp/form_helpers.html#%E8%A4%87%E9%9B%91%E3%81%AA%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B

● https://blog.dakatsuka.jp/2011/09/30/nested-has-one.html