rails named_scope ignores eager loading

Posted by Craig on Stack Overflow See other posts from Stack Overflow or by Craig
Published on 2010-06-13T22:27:44Z Indexed on 2010/06/13 22:32 UTC
Read the original article Hit count: 359

Two models (Rails 2.3.8):

  • User; username & disabled properties; User has_one :profile
  • Profile; full_name & hidden properties

I am trying to create a named_scope that eliminate the disabled=1 and hidden=1 User-Profiles. The User model is usually used in conjunction with the Profile model, so I attempt to eager-load the Profile model (:include => :profile).

I created a named_scope on the User model called 'visible':

named_scope :visible, {
  :joins => "INNER JOIN profiles ON users.id=profiles.user_id",
  :conditions => ["users.disabled = ? AND profiles.hidden = ?", false, false]
}

I've noticed that when I use the named_scope in a query, the eager-loading instruction is ignored.

Variation 1 - User model only:

 # UserController
 @users = User.find(:all)

 # User's Index view
 <% for user in @users %>
  <p><%= user.username %></p>
 <% end %>

 # generates a single query:
 SELECT * FROM `users`

Variation 2 - use Profile model in view; lazy load Profile model

 # UserController
 @users = User.find(:all)

 # User's Index view
 <% for user in @users %>
  <p><%= user.username %></p>
  <p><%= user.profile.full_name %></p>
 <% end %>

 # generates multiple queries:
  SELECT * FROM `profiles` WHERE (`profiles`.user_id = 1) ORDER BY full_name ASC LIMIT 1
  SHOW FIELDS FROM `profiles`
  SELECT * FROM `profiles` WHERE (`profiles`.user_id = 2) ORDER BY full_name ASC LIMIT 1
  SELECT * FROM `profiles` WHERE (`profiles`.user_id = 3) ORDER BY full_name ASC LIMIT 1
  SELECT * FROM `profiles` WHERE (`profiles`.user_id = 4) ORDER BY full_name ASC LIMIT 1
  SELECT * FROM `profiles` WHERE (`profiles`.user_id = 5) ORDER BY full_name ASC LIMIT 1
  SELECT * FROM `profiles` WHERE (`profiles`.user_id = 6) ORDER BY full_name ASC LIMIT 1

Variation 3 - eager load Profile model

  # UserController
  @users = User.find(:all, :include => :profile)

  #view; no changes

  # two queries
  SELECT * FROM `users` 
  SELECT `profiles`.* FROM `profiles` WHERE (`profiles`.user_id IN (1,2,3,4,5,6)) 

Variation 4 - use name_scope, including eager-loading instruction

  #UserConroller
  @users = User.visible(:include => :profile)

  #view; no changes

  # generates multiple queries
  SELECT `users`.* FROM `users` INNER JOIN profiles ON users.id=profiles.user_id WHERE (users.disabled = 0 AND profiles.hidden = 0) 
  SELECT * FROM `profiles` WHERE (`profiles`.user_id = 1) ORDER BY full_name ASC LIMIT 1
  SELECT * FROM `profiles` WHERE (`profiles`.user_id = 2) ORDER BY full_name ASC LIMIT 1
  SELECT * FROM `profiles` WHERE (`profiles`.user_id = 3) ORDER BY full_name ASC LIMIT 1
  SELECT * FROM `profiles` WHERE (`profiles`.user_id = 4) ORDER BY full_name ASC LIMIT 1

Variation 4 does return the correct number of records, but also appears to be ignoring the eager-loading instruction.

Is this an issue with cross-model named scopes? Perhaps I'm not using it correctly.

Is this sort of situation handled better by Rails 3?

© Stack Overflow or respective owner

Related posts about ruby-on-rails

Related posts about named-scope