如何在具有 multiple-directional 关联的 Rails 中创建三重嵌套 objects
How to create triple nested objects in Rails with multiple-directional associations
一篇名为 Triple nested Forms in Rails 的文章很好地描述了如何创建用于保存三个嵌套 objects 的表单。给出的示例是创建一个 Show
,has_many Seasons
,每个 Season
has_many Episodes
。另外,Episode --> belongs_to --> Season --> belongs_to --> Show
。
节目是这样创建的:
def new
@show = Show.new
@show.seasons.build.episodes.build
end
表单如下所示:
<%= form.fields_for :seasons do |s| %>
<%= s.label :number %>
<%= s.number_field :number %> <%= s.fields_for :episodes do |e| %>
<%= e.label :title %>
<%= e.text_field :title %>
<% end %>
<% end %>
<% end %>
这看起来很简单,因为所有关联 运行 都指向一个方向。我正在尝试做一些类似但更复杂的事情。我有一个 Parent
模型,其中每个 Parent
有多个 Children
,每个 Child
都注册了一个 School
。在指定 Children
是 Child
的复数后,关联必须是这样的:
Parent has_many Children, accepts_nested_attributes_for :children
Child belongs_to Parent, belongs_to School, accepts_nested_attributes_for :school
School has_many Children, accepts_nested_attributes_for :children
从图形上看,它看起来像这样:
Parent <-- belongs_to <-- Child --> belongs_to --> School
每个 Parent 也与一个用户相关联,如下所示:
User has_many :parents
Parents、Children 和学校的数据在以下表格中输入(使用简单表格 gem 生成),其中学校作为下拉列表输入从学校 table:
填充的选择器
@schools = School.all
<%= simple_form_for (@parent) do |f| %>
<%= f.input :name, label: 'name' %>
<%= f.simple_fields_for :children, @children do |child_form| %>
<%= child_form.input :name, label: "Child Name" %>
<%= child_form.simple_fields_for :school, @school do |school %>
<%= school.collection_select :id, @schools, :id, :name, {}, {} %>
<% end %>
<% end %>
<% end %>
我设置了 new
控制器方法来创建一个 Parent
,在现有的 School
中注册了三个 Children
。然后我尝试将 Children
与 schools
table 中已经存在且 id = 1 的 School
相关联。
def new
@parent = Parent.new
# creating 3 children
@children = Array.new(3) {@parent.children.build}
@school = School.find(1)
@school.children.build
end
这会引发错误
Couldn't find School with ID=1 for Child with ID=
错误位于创建方法的第一行,如下所示:
def create
@parent = Parent.new(parent_params.merge(:user => current_user))
if @parent.save
redirect_to root_path
else
render :new, :status => :unprocessable_entity
end
end
def parent_params
params.require(:parent).permit(:name, :child_attributes => [:id, :name, age, :school_attributes => [:id, :name]])
end
由于错误文本指出 "Child with ID= "
,因此必须在分配新 Children
的 ID 之前抛出错误。为什么ID=1的School
存在于schools
table中却找不到?或者,这是否意味着 School
记录在尝试保存该实例之前未与 Child
的实例正确关联?如果是这样,我该如何修复关联?
最常见的 missconceptions/misstakes 嵌套属性之一是认为您需要它来进行简单的关联分配。你不知道。您只需要将 id 传递给 assocation_name_id=
setter.
如果使用嵌套属性甚至根本无法满足您的要求。它不会在您执行 child.school_attributes = [{ id: 1 }]
时从现有记录创建关联,而是会尝试创建新记录或更新现有学校记录。
如果用户同时创建学校,您只需要接受学校的嵌套属性。在那种情况下,使用 Ajax 而不是将所有内容都塞进一个大型动作中可能是一个更好的主意。
<%= simple_form_for (@parent) do |f| %>
<%= f.input :name, label: 'name' %>
<%= f.simple_fields_for :children, @children do |child_form| %>
<%= child_form.input :name, label: "Child Name" %>
<%= child_form.associaton :school,
collection: @schools, label_method: :name %>
<% end %>
<% end %>
def parent_params
params.require(:parent).permit( :name,
child_attributes: [:id, :name, :age, :school_id]]
)
end
一篇名为 Triple nested Forms in Rails 的文章很好地描述了如何创建用于保存三个嵌套 objects 的表单。给出的示例是创建一个 Show
,has_many Seasons
,每个 Season
has_many Episodes
。另外,Episode --> belongs_to --> Season --> belongs_to --> Show
。
节目是这样创建的:
def new
@show = Show.new
@show.seasons.build.episodes.build
end
表单如下所示:
<%= form.fields_for :seasons do |s| %>
<%= s.label :number %>
<%= s.number_field :number %> <%= s.fields_for :episodes do |e| %>
<%= e.label :title %>
<%= e.text_field :title %>
<% end %>
<% end %>
<% end %>
这看起来很简单,因为所有关联 运行 都指向一个方向。我正在尝试做一些类似但更复杂的事情。我有一个 Parent
模型,其中每个 Parent
有多个 Children
,每个 Child
都注册了一个 School
。在指定 Children
是 Child
的复数后,关联必须是这样的:
Parent has_many Children, accepts_nested_attributes_for :children
Child belongs_to Parent, belongs_to School, accepts_nested_attributes_for :school
School has_many Children, accepts_nested_attributes_for :children
从图形上看,它看起来像这样:
Parent <-- belongs_to <-- Child --> belongs_to --> School
每个 Parent 也与一个用户相关联,如下所示:
User has_many :parents
Parents、Children 和学校的数据在以下表格中输入(使用简单表格 gem 生成),其中学校作为下拉列表输入从学校 table:
填充的选择器@schools = School.all
<%= simple_form_for (@parent) do |f| %>
<%= f.input :name, label: 'name' %>
<%= f.simple_fields_for :children, @children do |child_form| %>
<%= child_form.input :name, label: "Child Name" %>
<%= child_form.simple_fields_for :school, @school do |school %>
<%= school.collection_select :id, @schools, :id, :name, {}, {} %>
<% end %>
<% end %>
<% end %>
我设置了 new
控制器方法来创建一个 Parent
,在现有的 School
中注册了三个 Children
。然后我尝试将 Children
与 schools
table 中已经存在且 id = 1 的 School
相关联。
def new
@parent = Parent.new
# creating 3 children
@children = Array.new(3) {@parent.children.build}
@school = School.find(1)
@school.children.build
end
这会引发错误
Couldn't find School with ID=1 for Child with ID=
错误位于创建方法的第一行,如下所示:
def create
@parent = Parent.new(parent_params.merge(:user => current_user))
if @parent.save
redirect_to root_path
else
render :new, :status => :unprocessable_entity
end
end
def parent_params
params.require(:parent).permit(:name, :child_attributes => [:id, :name, age, :school_attributes => [:id, :name]])
end
由于错误文本指出 "Child with ID= "
,因此必须在分配新 Children
的 ID 之前抛出错误。为什么ID=1的School
存在于schools
table中却找不到?或者,这是否意味着 School
记录在尝试保存该实例之前未与 Child
的实例正确关联?如果是这样,我该如何修复关联?
最常见的 missconceptions/misstakes 嵌套属性之一是认为您需要它来进行简单的关联分配。你不知道。您只需要将 id 传递给 assocation_name_id=
setter.
如果使用嵌套属性甚至根本无法满足您的要求。它不会在您执行 child.school_attributes = [{ id: 1 }]
时从现有记录创建关联,而是会尝试创建新记录或更新现有学校记录。
如果用户同时创建学校,您只需要接受学校的嵌套属性。在那种情况下,使用 Ajax 而不是将所有内容都塞进一个大型动作中可能是一个更好的主意。
<%= simple_form_for (@parent) do |f| %>
<%= f.input :name, label: 'name' %>
<%= f.simple_fields_for :children, @children do |child_form| %>
<%= child_form.input :name, label: "Child Name" %>
<%= child_form.associaton :school,
collection: @schools, label_method: :name %>
<% end %>
<% end %>
def parent_params
params.require(:parent).permit( :name,
child_attributes: [:id, :name, :age, :school_id]]
)
end