fields_forとaccepts_nested_attributes_for 重要

form_forで@itemとか渡してフォーム作ってる時に、アソシエーションやらの関係で別のモデルへの登録も同時にしたいということはよくあると思う。

 

itemに複数枚画像を関連づけることができるような場合、Itemモデルはhas_many :imagesでImageモデルはbelongs_to :itemでマイグレファイルにはt.bigint :item_id, foreign_key: true があるのはまあ普通だと思う。

 

ここからが親のモデルのフォーム内で子モデル登録するときの手順なんだけど、Itemモデルの方に

accepts_nested_attributes_for :images, allow_destroy:true

とか書いとく。allow_destroy:trueはオプションだけど、これを記述しとくと親のレコードが削除された時に子のレコードも削除してくれる。

 

そしてフォーム飛ばした先のクリエイトアクション、今回はitemsコントローラーのcreateアクションに

def new

@item = Item.new

@item.images.new

end

とか書いてimageクラスのインスタンス作る。ちなみにこれが作れてないとそもそもfileds_forの中のフォーム自体表示されなかったはず。

 

今度はストロングパラメータの書き方なんだけど、書き方に癖があるので注意。

def item_params

params.require(:item).permit(:name, :price, images_attributes: [:image])

end

 

フォームの書き方はこんな感じ

<%= form_for @item do |f| %>
  商品名<%= f.text_field :name %><br>
  価格<%= f.number_field :price %><br>
  <%= f.fields_for :images do |i| %>
    <%= i.file_field :image %><br>
  <% end %>
  <%= f.submit %>
<% end %>

 

流れをまとめると

普通にアソシエーション組む。

親モデルにaccepts_nested_attributes_for :テーブル名, allow_destroy:true

createアクション内でちゃんとどっちもインスタンス作っとく。

ストロングパラメタで許可 テーブル名_attributes: [:カラム名]

フォームの方でform_forの中にfields_for作ってやってその中に普通にフォーム作る。ちなみにfieldじゃなくてfilelds_forなので注意。

 

 

参考記事

ちょっと状況違うけど一応

https://qiita.com/hiroweb/items/74867433ab5091713521

field_for使う時は子のインスタンス作ってないと表示されない。(最初newじゃなくてcreateに記述してしまって入力フォームが出ないポカをやってしまった。)

https://acchann.hatenablog.com/entry/2018/03/16/224300

あとファイル内でaccepts_nested_attributes_forより先にモデルのリレーションが定義されてないと読み込み順で未定義エラーになるらしい

 

 

 

あとで書く