Rails name_scope 忽略急切加载
两种模型(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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
来自 railsapi.com:
我相信这回答了您的问题...“变体#4”中没有急切加载,因为您引用了
named_scope
上的profiles
表。From railsapi.com:
I believe this answers your question... there's no eager loading in "variation #4" because you references
profiles
table on yournamed_scope
.我相信以下内容可能会满足您的需求:
@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