rails named_scope忽略了急切的加载

时间:2010-06-13 22:27:44

标签: ruby-on-rails named-scope eager-loading

两个模型(Rails 2.3.8):

  • 用户;用户名和&残疾人财产;用户has_one:个人资料
  • 资料; full_name&隐藏属性

我正在尝试创建一个named_scope来消除disabled = 1和hidden = 1 User-Profiles。 User模型通常与Profile模型一起使用,因此我尝试加载Profile模型(:include =>:profile)。

我在User模型上创建了一个名为'visible'的named_scope:

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

我注意到当我在查询中使用named_scope时,会忽略eager-loading指令。

变体1 - 仅限用户模型:

 # 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`

变体2 - 在视图中使用Profile模型;延迟加载配置文件模型

 # 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

变化3 - 急切负载配置文件模型

  # 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)) 

变体4 - 使用name_scope,包括预先加载指令

  #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

变体4确实返回正确的记录数,但似乎也忽略了急切加载指令。

这是跨模型命名范围的问题吗?也许我没有正确使用它。

Rails 3是否更好地处理了这种情况?

2 个答案:

答案 0 :(得分:4)

来自railsapi.com

  

急切加载关联

     

[...]因为只加载了一个表   一次,条件或订单   不能引用除了以外的表   主要的。如果是这样的话   记录回落到之前   使用LEFT OUTER JOIN策略。   例如

     

Post.find(:all, :include => [ :author, :comments ], :conditions => ['comments.approved = ?', true])

     

将导致单个SQL查询   加入:LEFT OUTER   JOIN评论评论.post_id =   posts.id和LEFT OUTER JOIN作者   ON authors.id = posts.author_id。

我相信这会回答您的问题...“变体#4”中没有急切的加载,因为您引用了profiles上的named_scope表格。

答案 1 :(得分:0)

我相信以下内容可能会为您提供所需内容:

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

这对我来说很有用,但是我没有在命名范围的定义中加入其他表。

Jim Benton在他的博客上提供了一种优雅的方式将其添加到ActiveRecord:http://autonomousmachine.com/posts/2009/10/28/add-a-scope-for-easier-eager-loading