Products by Category

Hi,

I am trying to create a view where I can see all products that belong
to a certain category.
My models have the following relationships:
Category: has_many :products
Product: belongs_to :category

Both category_controller.rb and product_controller.rb are in my admin
area and need Admin login to edit/delete...

I am also using a catalog_controller.rb to view all my products.
In my catalog_controller.rb I created the following:

33. def categories
34. @page_title = "Categories"
35. @category_pages, @categories = paginate :categories, :per_page
=> 10
36. end
37.
38. def products_by_category
39. @category = Category.find(params[:id])
40. @page_title = @category.name
41. @products = Category.products.find(:all)
42. end

So, If I go to /catalog/categories, I can see a list of links to all
my categories.
My views/catalog/categories.rhtml has this code:
  <ul id="categories">
    <% for category in @categories %>
    <li><%= link_to category.name, :action =>
"products_by_category", :id => category %></li>
    <% end %>
  </ul>

My views/catalog/products_by_category.rhtml has this code:
<% if category.products? %>
<p>No Products found for this category</p>
<% else %>
<ul id="products">
  <% for product in @products %>
  <li><%= link_to product.title, :action => "show", :id => product %></

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

But when I click on the category link, I get a NoMethodError in
CatalogController#products_by_category:

undefined method `products' for Category:Class
Method missing
app/controllers/catalog_controller.rb:41:in `products_by_category'

What am I doing wrong?
Would you recommend a better way of achieving this?

TIA,
Elle


Try this instead
38. def products_by_category
39. @category = Category.find(params[:id])
40. @page_title = @category.name
41. @products = @category.products
42. end

If you want them in a certain order, add :order to your has_many
relationship.

elle wrote:

btw, you can also eliminate @products altogether and just use
@category.products.each do |product| in your view.

William Pratt wrote:

btw, the reason that Category.products didn’t work is because the
has_many association adds an instance method to the Category model, not
a class method, so Category.products is not a method, but c =
Category.find(:first); c.products is.

Hope this helps.

elle wrote:

Thanks William. The first solution didn't work. And when I try the
second one like this:

<% if category.products? %>
<p>No Products found for this category</p>
<% else %>
<ul id="products">
  <% @category.products.each do |product| %>
  <li><% product %></li>
  <% end %>
</ul>
<% end %>

I get a NameError:
undefined local variable or method `category' for #<#<Class:0x3260810>:
0x3260748>
Extracted source (around line #1)...

Again, what am I doing wrong??

Thanks,
Elle

You need the @ in front of category as it’s an instance variable…so
it woud be

<% unless @category.products.empty? -%>

<p>No Products found for this category</p>

<% else %>

<ul id="products">

<% @category.products.each do |product| %>

    <li><% product %></li>

<% end %>

</ul>

<% end %>

elle wrote:

Sorry…my bad

<% if @category.products.empty? -%>

<p>No Products found for this category</p>

<% else %>

<ul id="products">

<% @category.products.each do |product| %>

    <li><% product %></li>

<% end %>

</ul>

<% end %>

William Pratt wrote:

I now get a 500 Internal Server Error.
Would you know why?
Elle

No, sorry. My mistake.
I get: No products found for this category - even though I know for a
fact that I do have products for the category.

It's hard to say without seeing your full controller method, and your
category / product models and your database data. Try using
script/console to play around with the relationships and debug it there.

Try:
/script/console

then:
c = Category.find(1) #(or whatever category id you are looking at now)

then:
c.products

None-the-less, playing with it in script/console will help you narrow it
down, and if it works as expected there, the problem must be in your
controller and / or view code somewhere.

elle wrote:

It worked in the console. I changed it to this:
<%= product.title %>
and it worked. :))))

I then changed it to be a link: <%= link_to product.title, :action =>
"show", :id => product %>

Thank you so much :slight_smile:
Elle

You *can* do that, but I don't advise it. I think the code is easier to
deal with when all queries are executed in the controller, and all the
view does is render things that the controller has set up. It certainly
makes your code easier to test, since you can check assigns(:products)
in the test method.

Could I ask (what might be a silly question) how would you run the
query in the controller? and how would I change my code in my view?

Elle

just use the version that I submitted before the referenced post.

In the controller:

@products = @category.products

Then in the view:

<% @products.each do |product| -%>

#do stuff with product

<% end -%>

Josh Susser wrote: