railsで中間テーブルも含んだformをつくるのになかなか苦労したので記事にしました。
誰かの役に立てば幸いです。
やりたいこと
- 母のデータはすでに登録してある
- 父を登録するときに子供の情報も同時に登録する
- そのときに母のデータも関連づけて保存
model
father.rb
has_many :children, dependent: :destroy
has_many :mothers, through: :children
accepts_nested_attributes_for :children, allow_destroy: true
mother.rb
has_many :children, dependent: :destroy
has_many :fathers, through: :children
child.rb
belongs_to :father
belongs_to :mother
controller
father_controller.rb
private
def father_params
params.require(:father).permit(:name, :children_attributes: [:id, :mother_id, :age, :_destroy])
end
view
new.html.haml
= simple_form_for @father do |f|
= f.input :name, place_holder: "名前", label: false
- @mothers.each do |mother|
= f.simple_fields_for :children, mother.children.build do |tif| # 母と紐付け
= tif.input :age, place_holder: "子供の年齢", label: false
= tif.input :mother_id, as: :hidden, value: mother.id # ここで母親のidを子に持たせる
= f.submit "登録"
これで父と母、両方の情報を持った子が登録できます。
RSpec
テストでの情報の作り方に一番苦労した…
なぜかというと、上のnewの画面では登録ボタン→確認画面→同意ボタン→登録という流れを取っていました。
つまり、事前にデータを用意せず、テストの中で情報を登録させる必要がありました。
以下、テストコードになります。
children_spec.rb
require 'rails_helper'
feature '親子情報の登録' do
context '親子情報が有効な場合' do
let!(:mother) { create(:mother, name: "花子") }
let!(:father){ Father.new(name: "太郎") } # この時点ではまだcreateさせない
before { visit (登録画面のurl) }
scenario '登録できること' do
father.children.build(mother: mother, father: father, age: 1) # ここで子供の情報をbuildさせ
father.save # ここで同時に保存
# 以降保存できていることの処理
end
end
end
肝はデータをつくるときにcreateでなくnew→saveの流れを取ること。
ここでは同時保存の流れを取っているため、createさせてしまうと子情報を持たないfatherが作られてしまい、いくら子をbuildさせても同時保存されず、fatherがnilになってしまう。
複雑に絡んだテーブルだったためなかなか時間を割いたが、冷静に考えれば単純なことだった…