Topic: Nested attributes - not working in forms

Hi!

First of all I want to apologize for this long post. I just wanted to provide all information - on the one hand to make the error reproducable, on the other hand to enable people who maybe have the same problem in the future to get step-by-step instructions.

I try to generate a form with an 1:many-model. Doing so, I found the command accepts_nested_attributes_for. However, in my form only the parent-model gets saved, while the child-model stays empty.

I started by creating a project (I called it PhoneBook) in Rails 4.
Then I generated two models and started the migration:

$ rails g model person name
      invoke  active_record
      create    db/migrate/20130710205309_create_people.rb
      create    app/models/person.rb
      invoke    test_unit
      create      test/models/person_test.rb
      create      test/fixtures/people.yml
$ rails g model phone person_id:integer phone_number:integer
      invoke  active_record
      create    db/migrate/20130710205415_create_phones.rb
      create    app/models/phone.rb
      invoke    test_unit
      create      test/models/phone_test.rb
      create      test/fixtures/phones.yml
$ rake db:migrate
==  CreatePeople: migrating ===================================================
-- create_table(:people)
   -> 0.0008s
==  CreatePeople: migrated (0.0008s) ==========================================

==  CreatePhones: migrating ===================================================
-- create_table(:phones)
   -> 0.0007s
==  CreatePhones: migrated (0.0008s) ==========================================

Next, I specified the models

# app/models/person.rb
class Person < ActiveRecord::Base

  has_many :phones

  accepts_nested_attributes_for :phones

end
# app/models/phone.rb
class Phone < ActiveRecord::Base

  belongs_to :person

end

Then I created a controller for person

$ rails g controller people
      create  app/controllers/people_controller.rb
      invoke  erb
      create    app/views/people
      invoke  test_unit
      create    test/controllers/people_controller_test.rb
      invoke  helper
      create    app/helpers/people_helper.rb
      invoke    test_unit
      create      test/helpers/people_helper_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/people.js.coffee
      invoke    scss
      create      app/assets/stylesheets/people.css.scss

This controller was filled with the following code

class PeopleController < ApplicationController
  def new
    @person = Person.new
    @person.phones.new
  end

  def create
    @person = Person.new(person_params)
    @person.save

    redirect_to people_path
  end

  def index
    @person = Person.all
  end

private

  def person_params
    params.require(:person).permit(:name, phone_attributes: [ :id, :phone_number ])
  end

end

Of course I also needed some views. This one just shows the list of all people with their phone numbers

<h1>Phone Book</h1>

<ul>

  <% @person.each do |person| %>
    <li>
      <h2>
        <%= person.name %>
      </h2>

      <ul>
        <% person.phones.each do |phone| %>
          <li>
            <%= phone.phone_number %>
          </li>
        <% end %>
      </ul>

    </li>
  <% end %>
</ul>

and this one is the [p]interesting part[/p]: here's my code for the form that should create new people and phone numbers:

<%= form_for :person, url: people_path do |f| %>
  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>

  <%= f.fields_for :phones do |f_phone| %>
    <div class="field">
      <p>
        <%= f_phone.label :phone_number %><br />
        <%= f_phone.text_field :phone_number %>
      </p>
    </div>
  <% end %>

  <p>
    <%= f.submit %>
  </p>
<% end %>

To make it work (well it doesn't work, but almost) I also need a route of course:

PhoneBook::Application.routes.draw do
  resources :people
end

Are you still with me? Great! Creating a new person with a phone number in the rails console works without any problems. However, when I try my form, only the Person gets stored, while the phone number is lost.

Can you help me with this problem? What am I doing wrong?

Do I maybe need a controller for phone_number? As it is only accessed via person, I thought I wouldn't need one.

Thank you in advance!

Last edited by Spindoctor (2013-07-10 19:42:15)