Rails name_scope 忽略急切加载

发布于 2024-09-06 04:30:39 字数 2732 浏览 5 评论 0原文

两种模型(Rails 2.3.8):

  • 用户;用户名和禁用属性;用户 has_one :profile 个人
  • 资料;全名&隐藏属性

我正在尝试创建一个named_scope来消除disabled=1和hidden=1用户配置文件。 User 模型通常与 Profile 模型结合使用,因此我尝试立即加载 Profile 模型(:include => :profile)。

我在用户模型上创建了一个名为“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时,急切加载指令将被忽略。

变体 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 模型;延迟加载 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 - 急切加载 Profile 模型

  # 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 能更好地处理这种情况吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

三生一梦 2024-09-13 04:30:39

来自 railsapi.com

关联的预加载

[...] 由于只加载了一张表
一次,条件或命令
不能引用除
主要的一个。如果是这种情况,请主动
记录回退到之前的状态
使用基于 LEFT OUTER JOIN 的策略。
例如

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

将导致单个 SQL 查询
沿以下方向连接: LEFT OUTER
在comments.post_id =上加入评论
posts.id 和 LEFT OUTER JOIN 作者
ONauthors.id = posts.author_id。

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

From railsapi.com:

Eager loading of associations

[...] Since only one table is loaded
at a time, conditions or orders
cannot reference tables other than the
main one
. If this is the case Active
Record falls back to the previously
used LEFT OUTER JOIN based strategy.
For example

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

will result in a single SQL query with
joins along the lines of: LEFT OUTER
JOIN comments ON comments.post_id =
posts.id and LEFT OUTER JOIN authors
ON authors.id = posts.author_id.

I believe this answers your question... there's no eager loading in "variation #4" because you references profiles table on your named_scope.

谈情不如逗狗 2024-09-13 04:30:39

我相信以下内容可能会满足您的需求:

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

这对我有用,但我不会加入与我的命名范围定义中的其他表。

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

I believe the following may give you what you are looking for:

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

This did the trick for me, but I'm not joining with other tables in the definition of my named scope.

Jim Benton provides an elegant way of adding this to ActiveRecord on his blog: http://autonomousmachine.com/posts/2009/10/28/add-a-scope-for-easier-eager-loading

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文