Association Methods


#1

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 %>

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

...

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?


#2

On Oct 13, 4:38 pm, Daniel removed_email_address@domain.invalid wrote:

end
<%= f.label :Brand %>

<%= f.collection_select(:brand, Brand.find(:all), :id, :name,
{:prompt => “please select one brand”}) %>

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] =>
where 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 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


#3

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.


#4

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}) %>

<%= brand.name %>
<% end %>

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


#5

On Oct 13, 7:32 pm, Daniel removed_email_address@domain.invalid wrote:

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>”

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


#6

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


#7

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

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

<%= brand.name %>
<% end %>

#8

On Oct 13, 6:54 pm, Paul Y. removed_email_address@domain.invalid
wrote:

<% 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


#9

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


#10

On Oct 13, 7:20 pm, Paul Y. removed_email_address@domain.invalid
wrote:

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 %>


#11

I’ve tried:

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


#12

So I’m using:

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


#13

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


#14

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?


#15

On Oct 13, 8:15 pm, Paul Y. removed_email_address@domain.invalid
wrote:

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