ken
ken

Reputation: 797

rails fields_for and nested attributes

I have difficulty to understand how to use fields_for and nested attributes. In order to understand it better, I created a repo, which is not working.

I read this, which is not helpful.

I am using:

Rails 4.0.1 Ruby 2.0.0-p247

Question:

  1. address form shown, why can not save to database?

  2. Why not work?

    jack = Person.create(name: 'Jack')

    jack.address.create(street: '12w 33st')

The main function is as below:

#model
class Person < ActiveRecord::Base
  has_one :address
  accepts_nested_attributes_for :address
end

class Address < ActiveRecord::Base
  belongs_to :person
end

view as below:

<%= form_for(@person) do |f| %>
  <% if @person.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@person.errors.count, "error") %> prohibited this person from being saved:</h2>

      <ul>
      <% @person.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :age %><br>
    <%= f.text_field :age %>
  </div>
  <div class="field">
    <%= f.label :gender %><br>
    <%= f.text_field :gender %>
  </div>
  <%= fields_for :address do |address_fields|%>
    Street:
    <%= address_fields.text_field :street%>
    Zip code:
    <%= address_fields.text_field :zip_code%>
  <% end %>  
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

nested attributes

Conclusion:

  1. has_one relation does not support person.address.create. That's only for has_many
  2. rails api for fields_for, is really not clear. It's just missing the whole controller part. Follow it will not make it.

Upvotes: 9

Views: 7862

Answers (1)

cschroed
cschroed

Reputation: 6834

Try:

jack = Person.create(name: 'Jack')
jack.create_address(street: '12w 33st')

When there is only one associated address (has_one :address) you use create_address. When there are many associated addresses (has_many :addresses) you use addresses.create.

Also, in your view you will want f.fields_for instead of fields_for.

I would recommend checking out the RailsCasts for Nested Model Form to learn more.

Additional Info (to address comments below)

Also note that create will make a new object and attempt to save it in the database. build will just build the new object. In your PeopleController you'll want something like this:

def new
  @person = Person.new
  @person.build_address
end

Based on your need, you can determine whether you want this build to occur in other actions or in callbacks in the model. But this will get you started.

You'll also need to update the person_params to

def person_params
  params.require(:person).permit(:name, :age, :gender, address_attributes: [:id, :street, :zip_code])
end

Upvotes: 9

Related Questions