Edited at

Railsでaccepts_nested_attributes_forを使ってNested Modelを新規作成するときの注意


Nested model (attributes)とは

Railsの超便利機能で、親と子の関係もしくはhas one関係をnestedと指定することで、

親を作成時に同時に子も作成してしまうことができる。

https://github.com/plataformatec/simple_form/wiki/Nested-Models

指定するためには下記のようにする。


app/models/parent.rb

class Parent < ApplicationRecord

has_many :children
accepts_nested_attributes_for :children
end


app/models/child.rb

class Child < ApplicationRecord

belongs_to :parent
end


Nested attributesでのモデル作成が失敗してしまう場合

例えば、parentを作成するフォームでnestedされたchildを一緒に作成することを考える。

フォームは下記のようになる。

<%= simple_form_for @parent, :html => { :class => 'form-horizontal' } do |f| %>

<%= f.input :name %>
<%= f.simple_fields_for :children, @parent.children.build do |c| %>
<%= c.input :name %>
<% end %>

<%= f.button :submit, :class => 'btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
parents_path, :class => 'btn btn-default' %>
<% end %>

しかし、このフォームで更新ボタンを押すと、Children parent must existとエラーを出してトランザクションがロールバックしてしまう。


理由と解決

処理中、Childrenを作成するときに、parent_idが必要だと主張する。

でもParentも今作成中なわけなので、DBに登録して初めて得られるIDは持っていない。

ここで、ParentChildrenの意見が相反してエラーが発生しトランザクションがロールバックする。

Rails 5.0ではこれを回避するための方法として、inverse_ofが導入されており、

親子両方のモデルにinverse_ofを設定することで、id違反を解消してくれる。

http://stackoverflow.com/questions/39090576/rails-5-error-message-child-model-parent-model-must-exist


app/models/parent.rb

class Parent < ApplicationRecord

has_many :children, inverse_of: :parent
accepts_nested_attributes_for :children
end


app/models/child.rb

class Child < ApplicationRecord

belongs_to :parent, inverse_of: :children
end


Railsアプリサンプル

下記に置きました。Parent->child->descendantsの3階層でのnestedを実現しようとして、

descendantsをちゃんと実装する前に力尽きました。でも、parent->childのnestedの更新は成功しています。

https://github.com/onelittlenightmusic/sample_nested_model_creation