在 Rails3 中使用范围进行预加载
我一直在尝试根据 Rails3 应用程序中的某些范围急切加载关联,但找不到任何解决方案。
我的应用程序具有以下模型:
class Project
has_many :entries
has_many :to_dos
class ToDo
has_may :entries
has_many :tasks
belongs_to :project
class Task
has_many :entries
belongs_to :to_do
class Entry
belongs_to :project
belongs_to :to_do
belongs_to :task
# options format: {:from_date=>(Date.today-1.week), :to_date=>(Date.today+1.week), :user_id=>60}
scope :filtered_list, lambda { |options|
condition = options[:user_id].nil? ? "true" : "user_id = #{options[:user_id]}"
condition += options[:from_date].nil? ? "" : " AND entry_date >= '#{options[:from_date]}'"
condition += options[:to_date].nil? ? "" : " AND entry_date <= '#{options[:to_date]}'"
where(condition)
}
在projects#index 中,我有以下代码来获取用户的所有项目:
@projects = current_user.projects.includes(:entries, :to_dos =>[:entries, :tasks => :entries])
它获取用户的所有项目,并立即加载关联。因此,当我执行以下循环来获取项目中的所有条目时,不会触发新的查询。
def all_entries(options)
entries = self.entries
self.to_dos.each do |d|
entries += d.entries
d.tasks.each do |t|
entries += t.entries
end
end
end
由于这种急切加载会获取所有条目,因此数据比我实际需要的数据太多。所以我尝试对急切加载的条目应用一些条件,但找不到任何解决方案。我一直在寻找类似的东西:
@projects = current_user.projects.includes(:entries.filtered_list(options), :to_dos =>[:entries.filtered_list(options), :tasks => :entries.filtered_list(options)])
这样只有满足某些条件的条目才会被加载。
我们不能将作用域与预加载一起使用吗? 请帮助我在范围界定的同时使用预加载。
I have been trying to eager load associations based on some scope in my rails3 app, but could not find any solution.
My app has following models:
class Project
has_many :entries
has_many :to_dos
class ToDo
has_may :entries
has_many :tasks
belongs_to :project
class Task
has_many :entries
belongs_to :to_do
class Entry
belongs_to :project
belongs_to :to_do
belongs_to :task
# options format: {:from_date=>(Date.today-1.week), :to_date=>(Date.today+1.week), :user_id=>60}
scope :filtered_list, lambda { |options|
condition = options[:user_id].nil? ? "true" : "user_id = #{options[:user_id]}"
condition += options[:from_date].nil? ? "" : " AND entry_date >= '#{options[:from_date]}'"
condition += options[:to_date].nil? ? "" : " AND entry_date <= '#{options[:to_date]}'"
where(condition)
}
And in projects#index i have following code to get all projects of an user:
@projects = current_user.projects.includes(:entries, :to_dos =>[:entries, :tasks => :entries])
It fetches all projects of the user, along with eager loading the associations. So when i perform following loop to get all the entries within the project, no new query gets fired.
def all_entries(options)
entries = self.entries
self.to_dos.each do |d|
entries += d.entries
d.tasks.each do |t|
entries += t.entries
end
end
end
As this eager loading fetches all entries, it is way too much data than what I actually needed. So I tried to apply some conditions to the entries eager loaded, but could not find any solution. I was looking for something like:
@projects = current_user.projects.includes(:entries.filtered_list(options), :to_dos =>[:entries.filtered_list(options), :tasks => :entries.filtered_list(options)])
So that only the entries satisfying some conditions get loaded.
Can't we use scoping with eager loading?
Please help me out use eagerloading alongside scoping.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
据我所知,范围不能应用于这样的包含关联。但是,您可以指定仅应用于预加载查询的条件。因此,通过一些重构,您可以拥有一个仅创建您当前在作用域中定义的条件的方法:
或者更红润一点:
然后将该方法链接到您的急切加载:
并且如果需要,还可以在您的作用域中重用它它独立:
免责声明:这些都没有用您的实际模型定义进行测试,但它与我周围的一些相当等效的模型一起工作得很好。
另请注意,如果过滤器选项最终来自客户端,则您的条件很容易受到 SQL 注入的攻击。
在幕后,Rails 使用 JOIN 来加载相关数据,因此需要注意这一点。这可能是一件好事(更少的查询),也可能是一件坏事(如果您的索引不是最优的)。这可能就是指南这样说的原因:
As far as I know, scopes cannot be applied to included associations like this. However, you can specify conditions that should only be applied to the eager loading queries. So with a bit of refactoring, you could have a method that only created the conditions you currently define in your scope:
or a bit more rubyesque:
and then chain that method to your eager loading:
and also reuse it in your scope if you need it independently:
Disclaimer: None of this is tested with your actual model definitions, but it works fine with some pretty equivalent ones that I had lying around.
Also note that if the filter options ultimately come from the client side, your condition is vulnerable to SQL injection.
Behind the scenes, Rails uses a JOIN to load the relevant data, so that is something to be aware of. It might be a good thing (a few less queries) or a bad thing (if your indexing is suboptimal). That's probably why the guide has this to say: