一つのフォームから複数のモデルを保存
何がしたいのか?
belongs_to/has_manyの関係を持つ2つのモデルのインスタンスをbelongs_to側のformから同時に作成したい。
view
form.fields_for のようにして params を入れ子にすることは避けた。理由はリレーションの親子関係が逆になってややこしくなると思ったため。今回は fields_for @school にして別のパラメータとしてコントローラに送るようにした。
<%= form_with(model: teacher, local: true) do |form| %> 中略 <div class="field"> <%= form.label :name, '名前' %> <%= form.text_field :name %> </div> 中略 <%= fields_for @school do |school_form| %> <div class="field"> <%= school_form.label :name, '学校名' %> <%= school_form.text_field :name %> </div> <% end %> <div class="actions"> <%= form.submit %> </div> <% end %>
コントローラ
belongs_to のリレーションがあると自動的にその属性で reqire :true になる。また、build_〇〇 というメソッドが使えるようになる。
@school = @teacher.build_school(school_params) @school.save
とすることで @school の生成と @teacher の school_id の取得を同時に行うことができた。
class TeachersController < ApplicationController def new @teacher = Teacher.new @school = School.new end def create @teacher = Teacher.new(teacher_params) create_school if @teacher.save reset_session session[:teacher] = @teacher.id redirect_to root_path else redirect_to root_path end end private def create_school @school = @teacher.build_school(school_params) unless @school.save redirect_to root_path end end def teacher_params params.require(:teacher).permit(:school_id, :name, :password, :password_confirmation, :email) end def school_params params.require(:school).permit(:name) end end
感想
コントローラに別のモデルを保存する処理を書いて良いものか少し迷った。TeachersControllerにSchoolsControllerを継承するという手もあるけどそれはそれで微妙な気がしたのでやめた。
追記
今回のパターンだと accepts_nested_attributes_for を使った方が良さそうです。下記のページでまとめました。