Как вызывать загрузку ассоциаций с current_user?

Я использую Devise для аутентификации в моем приложении Rails. Я бы хотел загрузить некоторые из моделей, связанных с пользователями, в некоторых моих контроллерах. Что-то вроде этого:

class TeamsController < ApplicationController

  def show
    @team = Team.includes(:members).find params[:id]
    current_user.includes(:saved_listings)

    # normal controller stuff
  end
end

Как я могу это достичь?

Ответ 1

Я столкнулся с той же проблемой, и хотя все говорят, что нет необходимости делать это, я обнаружил, что есть, как и вы. Так это работает для меня:

# in application_controller.rb:
def current_user
  @current_user ||= super && User.includes(:saved_listings).find(@current_user.id)
end

Обратите внимание, что это будет загружать ассоциации во всех контроллерах. Для моего случая использования, именно то, что мне нужно. Если вы действительно хотите это только в некоторых контроллерах, вам придется еще немного настроить его.

Это также вызовет User.find дважды, но с кешированием запросов это не должно быть проблемой, и поскольку оно предотвращает ряд дополнительных удалений БД, оно по-прежнему является усилением производительности.

Ответ 2

Переопределите serialize_from_session в вашей User модели.

class User
  devise :database_authenticatable

  def self.serialize_from_session key, salt
    record = where(id: key).eager_load(:saved_listings, roles: :accounts).first
    record if record && record.authenticatable_salt == salt
  end
end

Это, однако, будет загружать все запросы.

Ответ 3

Я хотел добавить то, что я считаю лучшим решением. Как отмечено в комментариях, существующие решения могут дважды поразить вашу БД с помощью запроса find. Вместо этого мы можем использовать ActiveRecord::Associations::Preloader чтобы использовать работу Rails вокруг ассоциаций загрузки:

def current_user
  @current_user ||= super.tap do |user|
    ::ActiveRecord::Associations::Preloader.new.preload(user, :saved_listings)
  end
end

Это позволит повторно использовать существующую модель в памяти вместо того, чтобы снова объединять и запрашивать всю таблицу.

Ответ 4

Почему бы не сделать это с помощью default_scope на модели?

так:

Class User  < ActiveRecord::Base
  ...
  default_scope includes(:saved_listings)
  ...
end