1

I have 3 models with "1 to n" associations, like this

Client --1 to n--> Category --1 to n--> Item

In one page, I need to display a list of Items along with their Categories. This page is subject to 3 level of filtering:

  • Client filtering: I know the client id (I'll use 'id=2' in this example)
  • Category name: dynamic filter set by the user
  • Item name: dynamic filter set by the user

And I'm getting more and more confused with ActiveRecord Associations stuff

In my ItemsController#index, I tried this:

categories = Client.find(2).categories
    .where('name LIKE ?', "%#{params[:filter_categories]}%")

@items = categories.items
    .where('name LIKE ?', "%#{params[:filter_items]}%")

The second line raises a NoMethodError undefined method 'items' for ActiveRecord::Relation. I understand that the first line returns a Relation object, but I cannot find a way to continue from here and get the list of Items linked to this list of Categories.

I also started to extract the list of categories ids returned by the first line, in order to use them in the where clause of the second line, but while writing the code I found it inelegant and thought there may be a better way to do it. Any help would be very appreciated. Thanks

models/client.rb

class Client < ActiveRecord::Base
  has_many :categories
  has_many :items, through: :categories
  ...
end

models/category.rb

class Category < ActiveRecord::Base
  belongs_to :client
  has_many :items
  ...
end

model/item.rb

class Item < ActiveRecord::Base
  belongs_to :category
  has_one :client, through: :category
  ...
end
4

2 回答 2

2

You can only call .items on a category object, not on a collection. This would work:

@items = categories.first.items
    .where('name LIKE ?', "%#{params[:filter_items]}%")

To get what you want, you can do the following:

@items = Item
    .where('category_id IN (?) AND name LIKE ?', categories, "%#{params[:filter_items]}%")

Assuming that in the end you are only interested in what is in @items, it would be even better to do it in one query instead of two, using joins:

@items = Item.joins(:category)
    .where('items.name LIKE ? AND categories.name = ? AND categories.client_id = 2', "%#{params[:filter_items]}%", "%#{params[:filter_categories]}%")
于 2013-03-13T13:00:21.093 回答
0

You can try smth like this:

item_ids = Client.find(2).categories.inject([]) { |ids, cat| ids |= cat.item_ids; ids }
items = Item.find(item_ids)

This how you can get a list of nested objects that associated through another table.

于 2013-03-13T13:02:36.413 回答