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:

  1. def categories
  2.  @page_title = "Categories"
    
  3.  @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:

    <% for category in @categories %>
  • <%= link_to category.name, :action => "products_by_category", :id => category %>
  • <% end %>

My views/catalog/products_by_category.rhtml has this code:
<% if category.products? %>

No Products found for this category

<% else %>
    <% for product in @products %>
  • <%= link_to product.title, :action => "show", :id => product %> <% end %>
<% 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:inproducts_by_category’

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

TIA,
Elle

Try this instead

  1. def products_by_category
  2. @category = Category.find(params[:id])
    
  3. @page_title = @category.name
    
  4. @products = @category.products
    
  5. end

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

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

On Sep 16, 1:30 pm, William P. [email protected] wrote:

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

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

<% if category.products? %>

No Products found for this category

<% else %>
    <% @category.products.each do |product| %>
  • <% product %>
  • <% end %>
<% 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? -%>

No Products found for this category


<% else %>

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

  • <% end %>

<% end %>

William P. wrote:

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

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.


Josh S.
http://blog.hasmanythrough.com

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 S. wrote:

in the test method.


Josh S.
http://blog.hasmanythrough.com


Sincerely,

William P.

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.

Sorry…my bad

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

No Products found for this category


<% else %>

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

  • <% end %>

<% end %>

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.

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

On Sep 16, 2:23 pm, William P. [email protected] wrote:

<% end %>
I now get a 500 Internal Server Error.
Would you know why?
Elle

On Sep 16, 2:42 pm, William P. [email protected] wrote:

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.

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

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.