Нетерпеливая загрузка с областью видимости в 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)
}

И в проектах # 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)])

Так что загружаются только записи, удовлетворяющие некоторым условиям.

Разве мы не можем использовать область видимости с нетерпеливой загрузкой? Пожалуйста, помогите мне использовать нетерпеливую загрузку вместе с областью видимости.


person jayandra    schedule 18.07.2011    source источник


Ответы (1)


Насколько мне известно, области действия нельзя применять к включенным ассоциациям, подобным этой. Однако вы можете указать условия, которые должны применяться только к запросам с нетерпеливой загрузкой. Таким образом, после небольшого рефакторинга у вас может быть метод, который создает только те условия, которые вы в настоящее время определяете в своей области:

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

или немного более 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

а затем привяжите этот метод к вашей нетерпеливой загрузке:

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

а также повторно использовать его в своей области, если вам это нужно независимо:

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

Отказ от ответственности: ничто из этого не тестировалось с вашими фактическими определениями модели, но оно отлично работает с некоторыми довольно эквивалентными, которые у меня были.

Также обратите внимание, что если параметры фильтра в конечном итоге поступают со стороны клиента, ваше состояние уязвимо для внедрения SQL.

За кулисами Rails использует JOIN для загрузки соответствующих данных, так что об этом нужно знать. Это может быть хорошо (на несколько запросов меньше) или плохо (если ваша индексация неоптимальна). Вероятно, поэтому в руководстве говорится следующее:

Несмотря на то, что Active Record позволяет задавать условия для активно загружаемых ассоциаций точно так же, как и для соединений, вместо этого рекомендуется использовать соединения.

person Thilo    schedule 05.08.2011