Rail Shot3

I tried to load associations based on some area in my rails3 application but could not find any solution.

My application has the 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 # index projects, I have the following code to get all user projects:

 @projects = current_user.projects.includes(:entries, :to_dos =>[:entries, :tasks => :entries]) 

It extracts all user projects and also downloads associations. Therefore, when I execute the next cycle to get all the records in the project, a new request does not start.

 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 

Since this downloaded download retrieves all records, this is too much data than what I really need. Therefore, I tried to apply some conditions for downloadable records, but could not find a 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 only records that satisfy certain conditions are loaded.

Is it possible to use scope with active loading? Please help me use eagerloading along with the scope.

+7
source share
1 answer

As far as I know, areas cannot be applied to such associations. However, you can specify conditions that should only apply to download requests. Therefore, with a little refactoring, you may have a method that only created the conditions that you currently define in your area:

 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 

or a little more rubyesque:

 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 

and then bind this method to your active boot:

 @projects = current_user.projects.includes(:entries, :to_dos =>[:entries, :tasks => :entries].where(Entry.filter_by(options)) 

and also reuse it in your area if you need it:

 scope :filtered_list, lambda { |options| where(Entry.filter_by(options)) } 

Disclaimer: None of these have been tested with your actual model definitions, but it works fine with some pretty equivalent ones that I was lying on.

Also note that if the filter parameters ultimately come from the client side, your state is vulnerable to SQL injection.

Behind the scenes, Rails uses JOIN to load the relevant data, so this is what you need to know about. It can be good (slightly fewer requests) or bad (if your indexing is suboptimal). This is probably why guide says the following:

Even though Active Record allows you to set conditions for those who wish, loaded associations, like associations, it is recommended that you use joins instead.

+1
source

All Articles