cocoonを用いて、動的に変化するフォームを作成した際にハマったことを記します。
cocoonの導入は以下の記事を参考にしました。
・【Rails】cocoonを用いて親子孫関係のテーブルに複数のデータを同時保存する方法 - Qiita
・【Rails6】cocoonを使った動的フォーム入力画面の作り方 - Qiita
#問題概要
以下のような3つのモデルを作成し、関連づけたと仮定します。
関係 | モデル名 |
---|---|
親 | Parent |
子 | Child |
孫 | Grandchild |
上記記事を参考にしながらフォームを作成し、できたフォームよりデータを送信したら、以下のようなバリデーションに引っかかりました。
Child Grandchild child can't be blank
Child Parent can't be blank
#原因
各モデルのvalidationの記述に問題がありました。
has_many :children, dependent: :destroy
accepts_nested_attributes_for :children
belongs_to :parent
has_many :grandchildren, dependent: :destroy
accepts_nested_attributes_for :grandchildren
validates :parent_id, presence: true
belongs_to :child
validates :child_id, presence: true
accepts_nested_attributes_for
によって、親作成時にその子供のデータも同時に作成できるようになります。
このとき、childのparent_id
やgrandchildのchild_id
は各データの保存と同時に、そのデータのidが格納されます。
従って、データを保存し終わるまでは各フォームから、
<%= hidden_field_tag :parent_id, @parent.id %>
<%= hidden_field_tag :child_id, @child.id %>
のようにインスタンス変数から直接値を代入するよう試みても、各データは保存される前でありidを持たないため、バリデーションのpresenceに引っかかってしまいます。
#解決策
方法は非常にシンプルで、validates :child_id, presence: true
とvalidates :parent_id, presence: true
を削除します。
#おわりに
accepts_nested_attributes_for
のような1行で大きな効果を有するコードは、その動作を理解できていないと、予想外のエラーの原因になります。便利なものほどその本質の理解を心がけようと思いました。