Nested Models

Francisco Quintero edited this page Sep 14, 2018 · 18 revisions

Nested Models

Nested Models let you build forms that update an object and its associated objects all in one shot.

Background

To use nested models with simple_form, there are two rails features that you have to understand:

  • accepts_nested_attributes_for - an ActiveRecord class method that goes in your model code - it lets you create and update child objects through the associated parent. An in depth explanation is available in the ActiveRecord documentation.
  • fields_for - a helper method that goes in your view code - it builds form fields for your nested models

An example

Let's take a simple example:

class Machine < ActiveRecord::Base
  has_many :parts , inverse_of: :machine
  accepts_nested_attributes_for :parts
end

class Part < ActiveRecord::Base
  # name:string
  belongs_to :machine
end

With these models, we can use simple_form to update the machine and its associated parts in a single form:

<%= simple_form_for @machine do |m| %>
  <%= m.simple_fields_for :parts do |p| %>
    <%= p.input :name %>
  <% end %>
<% end %>

For 'new' action, build the nested model from the controller:

class MachinesController < ApplicationController
  def new
    @machine = Machine.new
    @machine.parts.build
  end
end

In case of 1-1 relationship

e.g. if Machine has_one Color, accepts_nested_attributes_for must be called with :color.

accepts_nested_attributes_for :color

And in your Controller#new action, you should make a new instance for the :color attribute:

class MachinesController < ApplicationController
  def new
    @machine = Machine.new
    @machine.build_color
  end
end

If you have non-ActiveRecord models, then refer to Use with ActiveModel compliant models page.

Example for key-value associations

To fill the label of the value field with the key attribute and give it an ID so we can grab it with Capybara, we can access the object in question by calling nested_form.object

<%= simple_form_for @user do |m| %>
  <%= m.simple_fields_for :custom_fields do |c| %>
    <%= c.input :field_key, as: :hidden %>
    <%= c.input :field_value, label: c.object.field_key, input_html: { id: c.object.field_key } %>
  <% end %>
<% end %>

Example for Rails 4 Strong Prameters

If you need to get this working with Strong Parameters

Add the nested attributes in the form of [ATTRIBUTE IN PLURAL]_[ATTRIBUTES WORD]: [LIST OF SYMBOL ATTRIBUTES]. For the machines example would be: parts_attributes: [:id, :name]

class MachinesController < ApplicationController
  def machine_params
    params.require(:machine)
          .permit(:name,
                  :owner,
                  parts_attributes: [:id, :name])
  end
end

Do the normal thing in the view with simple_fields_for:

<%= simple_form_for @machine do |f| %>
  <%= f.simple_fields_for :parts, part do |pf| %>
    <%= pf.input :name %>
  <% end %>
<% end %>
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.