ミナミ(@minami_nakasato)です。
🔶今回の目標🔶
親子関係にある
Aテーブル/モデル(親)
Bテーブル/モデル(子)
これら2つに対して、同じビュー内のフォームからデータを登録しよう!
🔶やり方🔶
「一つのテーブルだけを扱うであれば
"form_for" や "form_with"
で簡単にできるけど」
「同じフォーム内で2つのテーブルのデータを変更するなんて」
「どうすればええんじゃろ〜」
そんなときに「fields_for」が役に立ちます!
今回は親である「niwatori」モデルと、子である「hiyoko」モデルを例に取り組んでみましょう💪
ステップ① モデルとマイグレーションファイルを編集
🐔親🐔
(a) モデルを作成
% rails g model Niwatori
(b) モデルファイルを編集
class Niwatori < ApplicationRecord
belongs_to :user #注1
has_one :hiyoko #注2
accepts_nested_attributes_for :hiyoko #注3
end
【注1】
Niwatoriのさらに親としてuserモデルがある設定です。養鶏場のオーナーだと思ってください。
【注2】
今回はあえて「has_one」を例に出して説明します。
「has_many」を例にした説明はネット上に沢山あるけど、「has_one」を例にした説明が少ないので。
【注3】
2つのモデルがアソシエーションで結びついている時、2つまとめてレコードを更新できるRailsメソッドです。
コントローラでの記述と組み合わせて使うので、これだけではまだ未完成。
(c) マイグレーションファイルを編集
先ほど「rails g model」コマンドを実行した結果、マイグレーションファイルも生成されたので記入しましょう。
class CreateNiwatoris < ActiveRecord::Migration[6.0]
def change
create_table :niwatoris do |t|
t.references :user, foreign_key: true
t.string :niwatoriname
t.timestamps
end
end
end
(d) テーブルを作成
% rails db:migrate
🐥子🐥
(a) モデルを作成
% rails g model Hiyoko
(b) モデルファイルを編集
class Hiyoko < ApplicationRecord
belongs_to :niwatori
end
(c) マイグレーションファイルを書く
class CreateHiyokos < ActiveRecord::Migration[6.0]
def change
create_table :hiyokos do |t|
t.references :niwatori, foreign_key: true
t.string :hiyokoname
t.timestamps
end
end
end
(d) テーブルを作成
% rails db:migrate
ステップ② ビューを編集する
フォームを組み込みたいファイルに以下を追記。
= form_with model: @niwatori,local: true do |form|
= form.text_field :niwatoriname
= form.fields_for :hiyoko, @niwatori.build_hiyoko do |hiyoko|
= hiyoko.text_field :hiyokoname
= form.submit "投稿"
「fields_for」の部分を
= f.fields_for :hiyoko do |hiyoko|
にしてしまうとフォームが表示されません!
必ず上記のように
= f.fields_for :hiyoko, @niwatori.build_hiyoko do |hiyoko|
と書きましょう。
ステップ③ コントローラを編集する
class NiwatorisController < ApplicationController
def new
@niwatori = Niwatori.new
@niwatori.build_hiyoko #注1
end
def create
@niwatori = Niwatori.create(niwatori_params) #注2
end
private
def niwatori_params
params.require(:niwatori)
.permit(:niwatoriname,
hiyoko_attributes:[ #注3
:id
:hiyokoname
])
.merge(user_id: current_user.id)
end
end
【注1】「build」ってなに?
親モデルとアソシエーションを組んでいる子モデルのインスタンスを生成できるメソッドです。
外部キー(niwatori_id)にあらかじめ値が入ったインスタンスを作れます。
これで「fields_for」を利用したフォームがちゃんと表示されるようになりました。
ただし注意が必要!
親と子が「1 対 多」、
つまり親が子を「has_many」している場合は
「親モデル.子モデル.build」
と書きますが、
親と子が「1 対 1」、
つまり親が子を「has_one」している場合は
「親モデル.build_子モデル」
と書きましょう!
ここを間違えると、「fields_for」のフォームがビューで表示されません!
【注2】
ストロングパラメータで値を引っ張ってきましょう
【注3】
子モデルの値を受け取るために、
「fields_forの引数_attributes」
という書き方をします。
🔶まとめ🔶
これで複数のモデルを一つのフォームで変更できるようになりました🐔🐥
この記事があなたの役に立ちますように!
======================================
ミナミ(@minami_nakasato)です。都内のベンチャー企業でwebデザイン/プログラミング/動画撮影や編集などをやっています。