jay*_*dra 7 scoping ruby-on-rails-3
我一直试图根据我的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)
}
Run Code Online (Sandbox Code Playgroud)
在项目#index中,我有以下代码来获取用户的所有项目:
@projects = current_user.projects.includes(:entries, :to_dos =>[:entries, :tasks => :entries])
Run Code Online (Sandbox Code Playgroud)
它获取用户的所有项目,以及急切加载关联.因此,当我执行以下循环以获取项目中的所有条目时,不会触发任何新查询.
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
Run Code Online (Sandbox Code Playgroud)
由于这种急切的加载会获取所有条目,因此它的数据量远远超过我实际需要的数据.所以我尝试将一些条件应用于急切加载的条目,但找不到任何解决方案.我在寻找类似的东西:
@projects = current_user.projects.includes(:entries.filtered_list(options), :to_dos =>[:entries.filtered_list(options), :tasks => :entries.filtered_list(options)])
Run Code Online (Sandbox Code Playgroud)
这样只有满足某些条件的条目才会被加载.
我们不能使用带有急切加载的范围?请帮助我在范围内使用eagerloading.
据我所知,范围不能应用于这样的包含关联。但是,您可以指定仅应用于预加载查询的条件。因此,通过一些重构,您可以拥有一个仅创建您当前在范围内定义的条件的方法:
def self.filter_by(options)
condition = options[:user_id].nil? ? "true" : "entries.user_id = #{options[:user_id]}"
condition += options[:from_date].nil? ? "" : " AND entries.entry_date >= '#{options[:from_date]}'"
condition += options[:to_date].nil? ? "" : " AND entries.entry_date <= '#{options[:to_date]}'
condition
end
Run Code Online (Sandbox Code Playgroud)
或者更红一点:
def self.filter_by(options)
conditions = []
conditions << "entries.user_id = #{options[:user_id]}" unless options[:user_id].nil?
conditions << "entries.entry_date >= '#{options[:from_date]}'" unless options[:from_date].nil?
conditions << "entries.entry_date <= '#{options[:to_date]}'" unless options[:to_date].nil?
conditions.join(" AND ")
end
Run Code Online (Sandbox Code Playgroud)
然后将该方法链接到您的急切加载:
@projects = current_user.projects.includes(:entries, :to_dos =>[:entries, :tasks => :entries].where(Entry.filter_by(options))
Run Code Online (Sandbox Code Playgroud)
如果您独立需要它,也可以在您的范围内重用它:
scope :filtered_list, lambda { |options| where(Entry.filter_by(options)) }
Run Code Online (Sandbox Code Playgroud)
免责声明:这些都没有用您的实际模型定义进行测试,但它可以与我周围的一些相当等效的模型一起正常工作。
另请注意,如果过滤器选项最终来自客户端,则您的条件很容易受到 SQL 注入的攻击。
在幕后,Rails 使用 JOIN 来加载相关数据,因此需要注意这一点。这可能是一件好事(更少的查询),也可能是一件坏事(如果您的索引不是最优的)。这可能就是为什么该指南这样说:
尽管 Active Record 允许您像联接一样在急切加载的关联上指定条件,但推荐的方法是使用联接。
| 归档时间: |
|
| 查看次数: |
294 次 |
| 最近记录: |