Как исправить проблему n + 1 запросов в ActiveRecord (Rails 3), когда также используется обратный вызов after_initialize? - PullRequest
4 голосов
/ 30 июня 2011

Модель:

class Project < ActiveRecord::Base
  has_many :user_roles 
  after_initialize :add_user_roles

  def add_user_roles
    UserRoles.all.each do |ur|
      self.user_roles << ur unless self.user_roles.include?(ur)
    end
  end
end

Оператор, который находит проекты:

@projects = Project.includes(:user_roles)

Итак, как вы видите, я говорю, чтобы включить в запрос ассоциацию ролей пользователя.Тем не менее, я все еще вижу проблему n + 1 запросов: он находит роли один раз для каждого проекта.

Если я удаляю использование self.user_roles из обратного вызова и просматриваю журналы, я вижу, что он находит проекты и их роли пользователей в 2 запросах - один для проектов и один для ролей, использующихproject_id in (1,2,3,4,5...,n).

Есть ли способ обойти это?

Позвольте мне немного уточнить: хотя я и хочу обойти свою конкретную ситуацию, если потребуется, я бы предпочел ответы, которые сосредоточены на том, как решить проблему в целом.Я способен написать kludge, чтобы получить данные в том состоянии, в котором я их хочу, без использования обратного вызова after_initialize и, следовательно, не сталкиваться с проблемой n + 1 запросов.Однако я бы предпочел не делать этого, поэтому я предпочитаю ответы на общую проблему, а не на моем конкретном примере.

Ответы [ 3 ]

6 голосов
/ 15 февраля 2012

взгляните на рельсы, жадно загружающиеся http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations

Вы можете загрузить ассоциацию при загрузке объекта, используя includes

User.find(2).includes(:assets)#will load all assets with user

или вы можете указать в модели, чтобы усилить нагрузку

приложение / модели / user.rb

class User< AR::Base
   has_many :posts,:include=>:comments
end

class Post < AR::Base
  has_many :comments
  belongs_to :user
end

сейчас u.posts будет загружать комментарии для каждого сообщения

1 голос
/ 15 февраля 2014

Даже загруженные с нетерпением ассоциации недоступны в after_initialize (они загружаются после , когда запись инициализируется).Посмотрите эту проблему Rails для некоторого обсуждения:

https://github.com/rails/rails/issues/13156

Относительно исходного вопроса: похоже, что каждый проект будет иметь одинаковый набор UserRole объектов.Я предполагаю, что есть has_many :through, который был продезинфицирован, но несмотря на это, как Project когда-либо оказывался без полного набора?Я не вижу, как Project и UserRole на самом деле связаны здесь - из того, что видно в примере, это:

class Project < ActiveRecord::Base
  def user_roles
    UserRole.all
  end
end

выполнит то же самое, что и after_initialize ...

1 голос
/ 30 июня 2011

Это, вероятно, вызвано обратным вызовом after_initialize, который запускается каждый раз при инициализации каждого из объектов. Если цель обратного вызова состоит в том, чтобы автоматически назначать каждую роль каждому пользователю (если он еще не назначен), тогда вы можете сделать это через фильтр before_save. Таким образом, код не будет выполняться при выполнении поиска Project.includes(:user_roles).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...