Topic: Loop through nested models

Hi,Hope someone can help me here - very new to rails and having some initial trouble.
My models are as follows:

 class Category < ActiveRecord::Base
  has_many :subcategories
  has_many :locations, :through => :subcategories
end
class Location < ActiveRecord::Base
  has_many :subcategories
  has_many :category, :through => :subcategories
end
class Subcategory < ActiveRecord::Base
  belongs_to :category
  belongs_to :location
end

I need to loop through these 3 models showing their names like this:

Category.name
   Location.name
      Subcategory.name
      Subcategory.name
      Subcategory.name
           (subcategory from another sub category)

Here is my controller:

class SubcategoriesController < ApplicationController
  def index
    @locations = Location.find(:all,
                                :joins => :subcategories,
                                :select =>
    @categories = Category.all
    @subcategories = Subcategory.all
  end
end

my view:

<div class="categorylist">
  <ul>
  <%= @categories.each do |category|%>
    <h3>
      <%=h link_to category.name, category %>
    </h3>
    <%= @locations.where(:category_id => category.id).each do |location|%> <--THIS NOT WORKING
    <h6>
      <%= link_to location.name, location %>
    </h6>
      HERE THE SUBCATEGORIES SHOULD PRINT FOR CATEGORY.ID AND LOCATION.ID AND BELOW THAT, MAYBE
                        AN INSTANCE OF ITSELF
    <% end %>
  <% end %>  
  </ul>  
</div>

Not sure if I am approaching this correctly, but it would make more sense to me to initialize the 3 collections instead of having multiple calls?

Any help appreciated have spent a lot of time googling and trying different solutions, but so far none have worked as I need.

Thanks Christian

Last edited by Zwerge (2012-11-24 13:33:37)

Re: Loop through nested models

First, there is a typo in one of your relations definitions:

class Location < ActiveRecord::Base
  has_many :subcategories
  has_many :category, :through => :subcategories #wrong
  #should be in plural if you didn't override the naming convention rules:
  has_many :categories, :through => :subcategories
end

Second, it would be better to use 'includes' instead of 'joins' to limit the database calls, - every time you need an attribute from a joined table there will be a DB call.
So try to use it as follows:

@categories = Category.includes(:locations)
#or like that
@subcategories = Subcategory.includes(:category, :location)

May be you should add 'group' option, because you can have double values (the same category can have 1 or more locations and visa versa).

See more example and details at Rails guides

Last edited by Javix (2012-11-30 06:48:21)

Re: Loop through nested models

Hi Javix,

Thanks for commenting, but it doesn't really solve my problem. I'll try and explain in other words.

I want to print subcategories where it has both the id of a location and the id of the category. It must match the 2 foreing keys at the same time. I cannot seem to find a solution making this possible, whatever i do it seems to only match the location or the category - never both at same time.

It's probably a very known solution im looking for an maybe I have stared myself blind on it at the moment.

Re: Loop through nested models

Setting  the values for category_id and location_id in Subcategories table is done by AR for you when you add a corrsponding collection object on one or another side:

@category.locations << @location #where @location is an object of Location model with all needed attributes
#or from the location side:
@location.categories << @category #where @category is an object of Category model with all needed attributes

See more details about that here: http://guides.rubyonrails.org/association_basics.html

Re: Loop through nested models

Hi again,

Yeah that much i get.

1st level is no problem - it's when i try to do:

category.locations.subcategories <-- here the subcategory only depends on the location, but i need it to depend on both category and location (both foreign keys inside the subcategory)

category.locations.subcategories.where(category_id: category.id).each <-- this is working, but its making a whole lot of queries that i do not believe should be necessary...

Seems that when i put the .where(category_id: category.id) it is not using the include...

my controller call: @categories = Category.all(:include => {:locations => [:subcategories]})

My model Subcategory has 2 belongs_to and they must both allways be triggered.

What I want:

Fruit
  France
    apple
      granny smith
    fig
  India
    pineapple
    banana
Meat
  India 
    cow
    chicken
-------------------------------------------------------------------------------
What I am currently getting:

Fruit
  India
    cow 
    chicken
    banana
    pineapple

Last edited by Zwerge (2012-12-06 08:41:55)