What am I doing wrong in this nested form?

I have these 2 models:

class User < ActiveRecord::Base   has_one :city   accepts_nested_attributes_for :city end

class City < ActiveRecord::Base   belongs_to :user end

This view:

<%= form_for :user,:url => users_path,:method => :post do |f| %>

  <%= f.fields_for :city_attributes do |b| %>   <%= b.collection_select :id,City.all,:id,:name %>   <% end %>

  <div class="field">     <%= f.label :name %><br />     <%= f.text_field :name %>   </div>   <div class="actions">     <%= f.submit %>   </div> <% end %>

So, I already have a list of cities added. I want to allow the user to select from one of the available cities in the select. But, when I submit the form, I get this error:

Couldn't find City with ID=1 for User with ID=

What am I doing wrong?

With accepts_nested attributes this should just be f.fields_for :city

Fred

If I put f.fields_for :city, this gets generated:

<select name="user[city][id]" id="user_city_id">

and I get an AssociationMismatch saying ActiveRecord::AssociationTypeMismatch: City(#36862620) expected, got Hash(#21169932)

If I put f.fields_for :city_attributes, this gets generated:

<select name="user[city_attributes][id]" id="user_city_attributes_id">

Which looks more like what the nested form would accept.

With accepts_nested_attributes you always just use the association name - you don't need to mangle things. Also I'm slightly confused by your models / form. Does a city really belong to a unique user ?

Fred

Ignore the model names and what they stand for :). They are only for example purpose.

So, if I make my view like this:

<%= form_for :user,:url => users_path,:method => :post do |f| %>

  <%= f.fields_for :city do |b| %>   <%= b.collection_select :id,City.all,:id,:name %>   <% end %>

  <div class="field">     <%= f.label :name %><br />     <%= f.text_field :name %>   </div>   <div class="actions">     <%= f.submit %>   </div> <% end %>

Then I get the association mismatch:

City(#37602432) expected, got ActiveSupport::HashWithIndifferentAccess(#32969988)

With these parameters:

{"commit"=>"Save User", "authenticity_token"=>"whatever", "utf8"=>"✓", "user"=>{"city"=>{"id"=>"1"}, "name"=>"as"}}

Ignore the model names and what they stand for :). They are only for example purpose.

So, if I make my view like this:

<%= form_for :user,:url => users_path,:method => :post do |f| %>

Oh, for nested attributes to work you need an actual instance of user, not the symbol :user

<%= f.fields_for :city do |b| %> <%= b.collection_select :id,City.all,:id,:name %> <% end %>

I'm not convinced that this will play well with nested attributes. nested attributes mostly wants to either create a new object or edit an existing one, whereas you want to choose from a list of objects and then edit one of those (to set the user_id)

Fred

If I try to pass an User instance, the select isn't displayed. Here's the view now:

<%= form_for @user,:url => users_path,:method => :post do |f| %>

  <%= f.fields_for :city do |b| %>   <%= b.collection_select :id,City.all,:id,:name %>   <% end %>

  <div class="field">     <%= f.label :name %><br />     <%= f.text_field :name %>   </div>   <div class="actions">     <%= f.submit %>   </div> <% end %>

I guess it's expecting me to build the city instance in the controller? The way I got it to work is by doing this in my model:

def city_attributes=(attribs)   self.city = City.find(attribs[:id]) end

Is there no other way?

I guess it's expecting me to build the city instance in the controller?

correct. field_for on an association that has nested attributes turned on renders its block for each associated object.

The way I got it to work is by doing this in my model:

def city_attributes=(attribs) self.city = City.find(attribs[:id]) end

Is there no other way?

It might be easier to just have a city_id virtual attribute rather than trying to bend accepts_nested_attributes_for (given that you are currently overriding all of the stuff it gives you)

Fred

But, how would having it as a virtual attribute help? It would not be saved to the database, so it's value would be lost.

But, how would having it as a virtual attribute help? It would not be saved to the database, so it's value would be lost.

because you'd define city_id= to be basically the same as the city_attributes method you have above. It's not very different to what you have but allows you to get rid of the accepts_nested_attributes and the fields_for

Fred