Doubly-nested models

Hi all,

I have three models, like so:

User belongs_to :person accepts_nested_attributes_for :person, :allow_destroy => false has_many :contacts

Person has_many :contacts accepts_nested_attributes_for :contacts, :allow_destroy => true

Contact belongs_to :person

And actually, it's a bit more complicated because Contact uses single- table polymorphism and may be a PhoneNumber or an Address. Different information would be filled in for each on the form, so right now I have separate partials for each of those.

My goal is to create both the User and the Person, as well as a Contact (if specified), when the first form is submitted. I've got it so it creates the User and the Person, but it doesn't set any of the data in the Person row. It won't create any Contacts at all.

# new.html.erb <h1>Sign up</h1>

<% form_for @user, :url => account_path do |f| %>   <%= f.error_messages %>   <%= render :partial => "form", :object => f %>   <p>     <%= f.submit "Register" %>   </p> <% end %>

# _form.html.erb <%= error_messages_for :user %> <div class="user">   <% form_for @user do |user_form| -%>

    <%= user_form.label :email %><br />     <%= user_form.text_field :email %><br />     <br />     <%= user_form.label :password, user_form.object.new_record? ? nil : "Change password" %><br />     <%= user_form.password_field :password %><br />     <br />     <%= user_form.label :password_confirmation, 'Confirm' %><br />     <%= user_form.password_field :password_confirmation %><br />     <br />     <%= render :partial => "person", :object => @person %>   <% end -%> </div> <br />

# _person.html.erb <div class="person">   <% form_for @person do |person_form| -%>

    <%= person_form.label :title %><br />     <%= person_form.text_field :title %><br />     <br />     <%= person_form.label :first_name %><br />     <%= person_form.text_field :first_name %><br />     <br />     <%= person_form.label :last_name %><br />     <%= person_form.text_field :last_name %><br />     <br />     <%= person_form.label :job_title %><br />     <%= person_form.text_field :job_title %><br />     <br />     <div id="contacts">       <%= render :partial => "address", :collection => @person.addresses %>       <%= render :partial => "phone_number", :collection => @person.phone_numbers %>     </div>     <p>       <%= add_contact_link :address %>       <%= add_contact_link :phone_number %>     </p>   <% end -%> </div>

# _phone_number.html.erb (_address is very similar) <div class="phone_number">   <% new_or_existing = phone_number.new_record? ? 'new' : 'existing' %>   <% prefix = "person[#{new_or_existing}_contact_attributes]" %>

  <% fields_for prefix, phone_number do |contact_form| -%>     <p>       <%= contact_form.hidden_field :type %>       Area + Phone <%= contact_form.text_field :value %><br/>       <%= remove_contact_link :phone_number, 'remove' %>     </p>   <% end -%> </div>

# users_controller.rb   def new     @user = User.new     @person = @user.build_person     @addresses = @person.addresses.build     @phone_numbers = @person.phone_numbers.build   end

  # POST /users   # POST /users.xml   def create     @user = User.new(params[:user])     if @user.save       flash[:notice] = 'User was successfully registered.'       redirect_back_or_default account_url     else       render :action => :new     end   end

Any thoughts? Apologies if things are a little unorthodox. I've been fiddling for hours.

Thanks, John

Thanks all, think I fixed it, mostly.

UsersController:   def create     @user = User.new(params[:user])     @person = @user.build_person(params[:user][:person])     @person.contacts_attributes = params[:person][:contacts]     if @user.save       flash[:notice] = 'User was successfully registered.'       redirect_back_or_default account_url     else       render :action => :new     end   end

  def new     @user = User.new     @person = @user.build_person     @person.addresses.build     @person.phone_numbers.build   end

new.html.erb: <h1>Sign up</h1>

<% form_for @user, :url => account_path do |f| %>   <%= f.error_messages %>   <%= render :partial => "form", :object => f %>   <p>     <%= f.submit "Register" %>   </p> <% end %>

_form.html.erb <%= error_messages_for :user %> <div class="user">   <% form_for @user do |user_form| -%>

    <%= user_form.label :email %>     <%= user_form.text_field :email %>     <br />     <%= user_form.label :password, user_form.object.new_record? ? nil : "Change password" %>     <%= user_form.password_field :password %>     <br />     <%= user_form.label :password_confirmation, 'Confirm' %>     <%= user_form.password_field :password_confirmation %>     <br />     <%= render :partial => "person", :object => @person %>   <% end -%> </div> <br />

_person.html.erb: <div class="person">   <% fields_for @person do |person_form| -%>

    <%= person_form.label :title %><%= person_form.select :title, Person::TITLES %>     <%= person_form.label :first_name %><%= person_form.text_field :first_name %>     <%= person_form.label :last_name %><%= person_form.text_field :last_name %><br />     <br />     <%= person_form.label :job_title %><br />     <%= person_form.text_field :job_title %><br />     <br />     <div id="contacts">       <%= render :partial => "address", :collection => @person.addresses %>       <%= render :partial => "phone_number", :collection => @person.phone_numbers %>     </div>     <p>       <%= add_contact_link :address %>       <%= add_contact_link :phone_number %>     </p>   <% end -%> </div>

_address.html.erb: <div class="address">   <% fields_for "person[contacts]", address do |contact_form| -%>     <p>       Address<br/>       <%= contact_form.hidden_field :type %><br/>       <%= contact_form.select :label, Address::LABELS, :selected => Address::LABELS.first %>       Street <%= contact_form.text_field :value %><br/>       Zip+4 <%= contact_form.text_field :zip_plus_four, :limit => 10 %>       <%= remove_contact_link :address, 'remove' %>     </p>   <% end -%> </div>

And phone number is about the same as that.

The only problem is that it won't save the contents of the :type hidden_field in the last partial. It fails to validate. That's a topic for another post, though, I suppose.