hrsetyono
hrsetyono

Reputation: 4464

Rails 4 - Add scope to included Model

I have this association

class Product
  has_many :categorizations
  has_many :categories, through: :categorization

  scope :first_five, lambda { limit(5) }
end

class Category
  has_many :categorizations
  has_many :products, through: :categorization
end

For each category, I want to get first five products using first_five scope defined above.

To minimize DB request, I use includes()

@categories = Category.includes(:products).all

But how do you add the scope to products? I don't want to use default_scope since it affects everything.

I can't find it on Google (or I just can't get the right search term for it)

Thanks

Upvotes: 2

Views: 249

Answers (3)

vee
vee

Reputation: 38645

The new syntax for lambda scope in Rails 4 is:

scope :first_five, -> { limit(5) }

To limit to five products in your AR query, you could do:

@categories = Category.includes(:products).merge(Product.first_five).all

The merge(Product.first_five) is going to add a left outer join on the limited five products.

Update:

The above code as commented is going to limit the categories. A possible solution to your original problem could be to add another has_many...through relation calling it limited_products on the Category model as:

has_many :limited_products, -> { limit(5) }, 
         through: :products, 
         source: :categorizations

With this Categeory.includes(:limited_products) should yield the expected result of limiting products to 5.

As you've suggested, add the source: :categorizations option to the newly added association limited_products. source and class_name should have been synonymous. Since the association we are defining is a has_many...through association which ignores class_name option, we should be using :source option instead of :class_name option. Other association options ignored in a has_many...through relation are :primary_key and :foreign_key.

Upvotes: 2

Bachan Smruty
Bachan Smruty

Reputation: 5734

@categories = Category.includes(:products).all

It will fetch all the categories and its related products. Here limit does not work for products.

Just an alternative way. In the Category model

has_many :products, through: :categorization, limit: 5

Now you need to make joins onto it, like the following.

@categories = Category.joins(:products).all

It returns all the category and upon taking each category.products, it will return five products for each category.

I know it is not the answer that you are looking for. But that what I have.

Alternative Way

In the Category model

has_many :products, through: :categorization

def get_products(limit=5)
  products[0...limit]
end

And in the controller

@categories = Category.joins(:products).all

@categories.each do |category|
  products = category.get_products
  # YOUR CODE GOES HERE
end

You will get 5 products for each category.

Upvotes: 2

Siva
Siva

Reputation: 8058

This will work

class Category
  has_many :categorizations
  has_many :products, -> { limit(5) },  through: :categorization
end

Upvotes: 1

Related Questions