Association Methods

I am a newbie of ruby on rails. And I have met a problem with model association.

I have 2 tables here, one is "item" the other is "brand", when I create one new item, I want to select one brand from the list.

Here are the models:

class Brand < ActiveRecord::Base   has_many :items end

class Item < ActiveRecord::Base   belongs_to :brand end

for item/new view <% form_for(@item) do |f| %>   <%= f.error_messages %>

  <p>     <%= f.label :Brand %><br />     <%= f.collection_select(:brand, Brand.find(:all), :id, :name, {:prompt => "please select one brand"}) %>   </p> ...

I can get the list successfully when create the new item, but when I press the "create" button, I've got the "Brand(#57323960) expected, got String(#21132310)" AssociationTypeMismatch error

I am not sure why I got the AssociationTypeMismatch error, and how can I handle this?

I sometimes find these helper methods quite confusing. The key is what parameters they are sending back to your controller when you hit the create button. Check your logs if you're unsure. The above line is probably sending   params[:item][:brand] => <brand-id> where <brand-id> is the id number for an existing brand. Another way to check is to look at the generated select tag; it will probably be <select name="item[brand]" ...> (note how 'params' builds its hash structure off the 'name' attribute).

You might want to change it to:      <%= f.collection_select(:brand_id, Brand.find(:all), :id, :name, {:prompt => "please select one brand"}) %>

It depends on what your 'create' controller is doing; I assume you're just instantiating a new item object and passing over the field values from 'params'. So if your object sees params[:item][:brand_id] you might get what you want - the <brand-id> will be inserted into the items.brand_id field. If it sees 'brand' as per the original, then I'm guessing it's trying to build the assocation via the Item#brand association method which probably expects a Brand object and not its id (in string form).

I always have to go back and check the docs. So what I've said above might not be totally right. Get to know how 'params' is generated. Writing stuff test-first on your functional tests will start you thinking like this too.

Daniel

Well, thank you very much Daniel :slight_smile: your information is quite useful

I checked my code, yes, it is like <select name="item[brand]" ...> I forgot to mentioned that, I have tried to use "<%= f.collection_select(:brand_id, Brand.find(:all), :id, :name, {:prompt => "please select one brand"}) %> " But, I have got another error "undefined method `brand_id' for #<Item: 0x6ce3258>" I assumed maybe there is another place I should take care of? in the items controller?

I was totally confused, sorry.

Just to make sure: do you have a brand_id integer field in your items table? What is the code for your controller - the thing you're posting to?

Daniel

Hi I have been following this discussion and I have added a brand_id:integer field to my items table. The create button now works and populates the brand column with a number.

I've added this to the item show view:

<% for brand in Brand.find(:all, :conditions => {:id => @item.id}) %>   <div>     <%= brand.name %>   </div> <% end %>

However some of the brand names are not the name I selected when I created the entry.

is this suppose to show the brands for a particular item? The above can't do that - you're selecting brands whose id matches the items id, the results will be unpredictable at best. @item.brand contains the brand you selected for the item.

Fred

I changed it to this, which appears to work correctly:

<% for brand in Brand.find(:all, :conditions => {:id => @item.brand_id}) %>   <div>     <%= brand.name %>   </div> <% end %>

So now in my brands view I'm trying to show all of the items associated with that brand.

The following returns all items:

<% for item in Item.find(:all) %>   <%= item.name %> <% end %>

But obviously I only want the items for a particular brand, so I tried:

<% for item in Item.find(:all, :conditions => {:brand_id => @brand.id}) %>   <%= item.name %> <% end %>

But I get an error

Removed the @ sign from :brand_id => @brand.id:

<% for item in Item.find(:all, :conditions => {:brand_id => brand.id}) %>   <%= item.name %> <% end %>

Which appears to have fixed things

Removed the @ sign from :brand_id => @brand.id:

<% for item in Item.find(:all, :conditions => {:brand_id => brand.id}) %> <%= item.name %> <% end %>

Which appears to have fixed things

While this works, things will be a lot more concise and less error prone if you use the associations, for example the above simplifies to

<% for item in brand.items %>    <%= item.name %> <% end %>

What if I only wanted to show the latest item added?

I've tried:

<% for item in brand.items.find(:last) %>   <%= item.name %> <% end %>

So I'm using:

<% for item in Item.find(:all, :conditions => {:brand_id => brand.id}, :limit => "1") %>   <%= item.name %> <% end %>

So I'm using:

find :last only exist in rails 2.1

<% for item in Item.find(:all, :conditions => {:brand_id => brand.id}, :limit => "1") %>

that doesn't return the last item, it shows the first one (however without an order specified that is not always well defined).

brand.items.find :first is the same as the above, you can add an order clause to get the last item.

Fred

I'm pretty sure I have 2.1 but I can't get :first or last to work in the format you gave. It must be my syntax. Will post it later unless you can provide a valid example?