2012-09-01 17:25:58 -04:00
Rails nested model forms
========================
2009-03-14 19:40:34 -04:00
2010-12-22 13:17:23 -05:00
Creating a form for a model _and_ its associations can become quite tedious. Therefore Rails provides helpers to assist in dealing with the complexities of generating these forms _and_ the required CRUD operations to create, update, and destroy associations.
2009-03-14 19:40:34 -04:00
2012-11-29 17:25:02 -05:00
After reading this guide, you will know:
2009-03-14 19:40:34 -04:00
2012-11-29 08:14:08 -05:00
* do stuff.
2009-03-14 19:40:34 -04:00
2012-09-01 17:25:58 -04:00
--------------------------------------------------------------------------------
2009-03-14 19:40:34 -04:00
2013-08-23 11:59:11 -04:00
NOTE: This guide assumes the user knows how to use the [Rails form helpers ](form_helpers.html ) in general. Also, it's **not** an API reference. For a complete reference please visit [the Rails API documentation ](http://api.rubyonrails.org/ ).
2009-03-14 19:40:34 -04:00
2012-09-01 17:25:58 -04:00
Model setup
-----------
2009-03-14 19:40:34 -04:00
To be able to use the nested model functionality in your forms, the model will need to support some basic operations.
2014-04-13 11:50:31 -04:00
First of all, it needs to define a writer method for the attribute that corresponds to the association you are building a nested model form for. The `fields_for` form helper will look for this method to decide whether or not a nested model form should be built.
2009-03-14 19:40:34 -04:00
2014-04-13 11:50:31 -04:00
If the associated object is an array, a form builder will be yielded for each object, else only a single form builder will be yielded.
2009-03-14 19:40:34 -04:00
2012-09-01 21:37:59 -04:00
Consider a Person model with an associated Address. When asked to yield a nested FormBuilder for the `:address` attribute, the `fields_for` form helper will look for a method on the Person instance named `address_attributes=` .
2009-03-14 19:40:34 -04:00
2012-09-01 17:25:58 -04:00
### ActiveRecord::Base model
2009-03-14 19:40:34 -04:00
2012-09-01 21:37:59 -04:00
For an ActiveRecord::Base model and association this writer method is commonly defined with the `accepts_nested_attributes_for` class method:
2009-03-14 19:40:34 -04:00
2012-09-01 17:25:58 -04:00
#### has_one
2009-03-14 19:40:34 -04:00
2012-09-01 17:08:06 -04:00
```ruby
2009-03-14 19:40:34 -04:00
class Person < ActiveRecord::Base
has_one :address
accepts_nested_attributes_for :address
end
2012-09-01 17:08:06 -04:00
```
2009-03-14 19:40:34 -04:00
2012-09-01 17:25:58 -04:00
#### belongs_to
2009-03-14 19:40:34 -04:00
2012-09-01 17:08:06 -04:00
```ruby
2009-03-14 19:40:34 -04:00
class Person < ActiveRecord::Base
belongs_to :firm
accepts_nested_attributes_for :firm
end
2012-09-01 17:08:06 -04:00
```
2009-03-14 19:40:34 -04:00
2012-09-01 17:25:58 -04:00
#### has_many / has_and_belongs_to_many
2009-03-14 19:40:34 -04:00
2012-09-01 17:08:06 -04:00
```ruby
2009-03-14 19:40:34 -04:00
class Person < ActiveRecord::Base
has_many :projects
accepts_nested_attributes_for :projects
end
2012-09-01 17:08:06 -04:00
```
2009-03-14 19:40:34 -04:00
2012-09-01 17:25:58 -04:00
### Custom model
2009-03-14 19:40:34 -04:00
2013-08-23 11:59:11 -04:00
As you might have inflected from this explanation, you _don't_ necessarily need an ActiveRecord::Base model to use this functionality. The following examples are sufficient to enable the nested model form behavior:
2009-03-14 19:40:34 -04:00
2012-09-01 17:25:58 -04:00
#### Single associated object
2009-03-14 19:40:34 -04:00
2012-09-01 17:08:06 -04:00
```ruby
2009-03-14 19:40:34 -04:00
class Person
def address
Address.new
end
2010-08-14 01:13:00 -04:00
2009-03-14 19:40:34 -04:00
def address_attributes=(attributes)
# ...
end
end
2012-09-01 17:08:06 -04:00
```
2009-03-14 19:40:34 -04:00
2012-09-01 17:25:58 -04:00
#### Association collection
2009-03-14 19:40:34 -04:00
2012-09-01 17:08:06 -04:00
```ruby
2009-03-14 19:40:34 -04:00
class Person
def projects
[Project.new, Project.new]
end
2010-08-14 01:13:00 -04:00
2009-03-14 19:40:34 -04:00
def projects_attributes=(attributes)
# ...
end
end
2012-09-01 17:08:06 -04:00
```
2009-03-14 19:40:34 -04:00
NOTE: See (TODO) in the advanced section for more information on how to deal with the CRUD operations in your custom model.
2012-09-01 17:25:58 -04:00
Views
-----
2009-03-14 19:40:34 -04:00
2012-09-01 17:25:58 -04:00
### Controller code
2009-03-14 19:40:34 -04:00
2011-06-13 15:00:13 -04:00
A nested model form will _only_ be built if the associated object(s) exist. This means that for a new model instance you would probably want to build the associated object(s) first.
2009-03-14 19:40:34 -04:00
2012-09-01 21:37:59 -04:00
Consider the following typical RESTful controller which will prepare a new Person instance and its `address` and `projects` associations before rendering the `new` template:
2009-03-14 19:40:34 -04:00
2012-09-01 17:08:06 -04:00
```ruby
2013-01-19 21:38:45 -05:00
class PeopleController < ApplicationController
2009-03-14 19:40:34 -04:00
def new
@person = Person.new
@person .built_address
2.times { @person .projects.build }
end
2010-08-14 01:13:00 -04:00
2009-03-14 19:40:34 -04:00
def create
@person = Person.new(params[:person])
if @person .save
# ...
end
end
end
2012-09-01 17:08:06 -04:00
```
2009-03-14 19:40:34 -04:00
2012-09-01 21:37:59 -04:00
NOTE: Obviously the instantiation of the associated object(s) can become tedious and not DRY, so you might want to move that into the model itself. ActiveRecord::Base provides an `after_initialize` callback which is a good way to refactor this.
2009-03-14 19:40:34 -04:00
2012-09-01 17:25:58 -04:00
### Form code
2009-03-14 19:40:34 -04:00
Now that you have a model instance, with the appropriate methods and associated object(s), you can start building the nested model form.
2012-09-01 17:25:58 -04:00
#### Standard form
2009-03-14 19:40:34 -04:00
Start out with a regular RESTful form:
2012-09-01 17:08:06 -04:00
```erb
2010-04-06 19:52:39 -04:00
< %= form_for @person do |f| %>
2009-03-14 19:40:34 -04:00
< %= f.text_field :name %>
< % end %>
2012-09-01 17:08:06 -04:00
```
2009-03-14 19:40:34 -04:00
This will generate the following html:
2012-09-01 17:08:06 -04:00
```html
2009-03-14 19:40:34 -04:00
< form action = "/people" class = "new_person" id = "new_person" method = "post" >
2012-03-10 05:30:39 -05:00
< input id = "person_name" name = "person[name]" type = "text" / >
2009-03-14 19:40:34 -04:00
< / form >
2012-09-01 17:08:06 -04:00
```
2009-03-14 19:40:34 -04:00
2012-09-01 17:25:58 -04:00
#### Nested form for a single associated object
2009-03-14 19:40:34 -04:00
2012-09-01 21:37:59 -04:00
Now add a nested form for the `address` association:
2009-03-14 19:40:34 -04:00
2012-09-01 17:08:06 -04:00
```erb
2010-04-06 19:52:39 -04:00
< %= form_for @person do |f| %>
2009-03-14 19:40:34 -04:00
< %= f.text_field :name %>
2010-08-14 01:13:00 -04:00
2010-05-28 10:46:12 -04:00
< %= f.fields_for :address do |af| %>
2011-06-13 15:00:13 -04:00
< %= af.text_field :street %>
2009-03-14 19:40:34 -04:00
< % end %>
< % end %>
2012-09-01 17:08:06 -04:00
```
2009-03-14 19:40:34 -04:00
This generates:
2012-09-01 17:08:06 -04:00
```html
2009-03-14 19:40:34 -04:00
< form action = "/people" class = "new_person" id = "new_person" method = "post" >
2012-03-10 05:30:39 -05:00
< input id = "person_name" name = "person[name]" type = "text" / >
2010-08-14 01:13:00 -04:00
2012-03-10 05:30:39 -05:00
< input id = "person_address_attributes_street" name = "person[address_attributes][street]" type = "text" / >
2009-03-14 19:40:34 -04:00
< / form >
2012-09-01 17:08:06 -04:00
```
2009-03-14 19:40:34 -04:00
2012-09-01 21:37:59 -04:00
Notice that `fields_for` recognized the `address` as an association for which a nested model form should be built by the way it has namespaced the `name` attribute.
2009-03-14 19:40:34 -04:00
When this form is posted the Rails parameter parser will construct a hash like the following:
2012-09-01 17:08:06 -04:00
```ruby
2009-03-14 19:40:34 -04:00
{
"person" => {
"name" => "Eloy Duran",
"address_attributes" => {
"street" => "Nieuwe Prinsengracht"
}
}
}
2012-09-01 17:08:06 -04:00
```
2009-03-14 19:40:34 -04:00
2013-08-23 11:59:11 -04:00
That's it. The controller will simply pass this hash on to the model from the `create` action. The model will then handle building the `address` association for you and automatically save it when the parent (`person`) is saved.
2009-03-15 11:21:56 -04:00
2012-09-01 17:25:58 -04:00
#### Nested form for a collection of associated objects
2009-03-15 11:21:56 -04:00
The form code for an association collection is pretty similar to that of a single associated object:
2012-09-01 17:08:06 -04:00
```erb
2010-04-06 19:52:39 -04:00
< %= form_for @person do |f| %>
2009-03-15 11:21:56 -04:00
< %= f.text_field :name %>
2010-08-14 01:13:00 -04:00
2010-05-28 10:46:12 -04:00
< %= f.fields_for :projects do |pf| %>
2011-06-13 15:00:13 -04:00
< %= pf.text_field :name %>
2009-03-15 11:21:56 -04:00
< % end %>
< % end %>
2012-09-01 17:08:06 -04:00
```
2009-03-15 11:21:56 -04:00
Which generates:
2012-09-01 17:08:06 -04:00
```html
2009-03-15 11:21:56 -04:00
< form action = "/people" class = "new_person" id = "new_person" method = "post" >
2012-03-10 05:30:39 -05:00
< input id = "person_name" name = "person[name]" type = "text" / >
2010-08-14 01:13:00 -04:00
2012-03-10 05:30:39 -05:00
< input id = "person_projects_attributes_0_name" name = "person[projects_attributes][0][name]" type = "text" / >
< input id = "person_projects_attributes_1_name" name = "person[projects_attributes][1][name]" type = "text" / >
2009-03-15 11:21:56 -04:00
< / form >
2012-09-01 17:08:06 -04:00
```
2009-03-15 11:21:56 -04:00
2012-09-01 21:37:59 -04:00
As you can see it has generated 2 `project name` inputs, one for each new `project` that was built in the controller's `new` action. Only this time the `name` attribute of the input contains a digit as an extra namespace. This will be parsed by the Rails parameter parser as:
2009-03-15 11:21:56 -04:00
2012-09-01 17:08:06 -04:00
```ruby
2009-03-15 11:21:56 -04:00
{
"person" => {
"name" => "Eloy Duran",
"projects_attributes" => {
"0" => { "name" => "Project 1" },
"1" => { "name" => "Project 2" }
}
}
}
2012-09-01 17:08:06 -04:00
```
2009-03-15 11:21:56 -04:00
2012-09-01 21:37:59 -04:00
You can basically see the `projects_attributes` hash as an array of attribute hashes, one for each model instance.
2009-03-15 11:21:56 -04:00
2014-04-13 11:50:31 -04:00
NOTE: The reason that `fields_for` constructed a hash instead of an array is that it won't work for any form nested deeper than one level deep.
2009-03-15 11:21:56 -04:00
2012-09-01 21:37:59 -04:00
TIP: You _can_ however pass an array to the writer method generated by `accepts_nested_attributes_for` if you're using plain Ruby or some other API access. See (TODO) for more info and example.