Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Eager loading a named_scope from an association in Rails

Is there any way to eager load a named_scope from an association?

I have my Article model:

class Article < ActiveRecord::Base
  has_many :comments
end

and my Comment model:

class Comment < ActiveRecord::Base
  belongs_to :article

  named_scope :approved, :conditions => { :approved => true }
  named_scope :unapproved, :conditions => { :approved => false }
end

I could eager load all comments for an article with:

@article = Article.find(params[:id], :include => :comments)

How can I do the same, but only for approved comments?

like image 909
Daniel Perez Alvarez Avatar asked Oct 15 '22 16:10

Daniel Perez Alvarez


2 Answers

It is not built-in to rails at this time, but Ryan Daigle created a plugin called utility_scopes that adds a with() scope so you can do:

Article.approved.with(:comments)

Blog Post: http://ryandaigle.com/articles/2008/8/20/named-scope-it-s-not-just-for-conditions-ya-know Github Repo: http://github.com/yfactorial/utility_scopes

[Updated per comment]

My bad. I didn't read well enough. I don't think there's anything that will let you call on an association named_scope like that. The closest thing I can think of would be creating a named_scope on Article:

class Article < ActiveRecord::Base
   named_scope :with_approved_comments, {:include => :comments, :conditions => ['comments.approved = ?', true]}
end

Hope this helps.

like image 106
Ryan Briones Avatar answered Oct 18 '22 10:10

Ryan Briones


The other answer didn't work for me since I have to load many associations and associations of those associations on some models. I found I had two options, change the association to have conditions and thus make an association for each scope I wanted or turn them into methods on the class that had the has_many association. I needed to do something along the lines of:

@newspaper = Newspaper.find params[:id], :include => {
  :articles => {
    :author => true,
    :source => true
  }
}

So in my example I changed:

class Newspaper < ActiveRecord::Base
  has_many :articles
end

class Article
  belongs_to :newspaper
  belongs_to :author
  has_one :source

  named_scope :private, :conditions => { :private => true }
  named_scope :public, :conditions => { :private => false }
end

to:

class Newspaper < ActiveRecord::Base
  has_many :articles

  def articles_public
    articles.reject &:private
  end

  def articles_private
    articles.select &:private
  end
end

class Article
  belongs_to :newspaper
  belongs_to :author
  has_one :source
end

I found this way to be preferable since I can now eager load articles when finding a newspaper and just change all article.private to article_private. Had I created two associations like:

has_many :articles_private, :class_name => 'Article', :conditions {:private => true}
has_many :articles_public, :class_name => 'Article', :conditions {:private => false}

I would have to eager load them both in cases where I needed all associated articles.

like image 37
Aaron Avatar answered Oct 18 '22 09:10

Aaron