Rails 4: Как использовать include() с where() для извлечения связанных объектов
Я не могу понять, как пользователю использовать метод .where()
для получения связанных данных модели. В этом примере Projects принадлежит_to Users...
class Project < ActiveRecord::Base
belongs_to :user
has_many :videos
end
class User < ActiveRecord::Base
has_many :projects
end
class ProjectsController < ApplicationController
def invite
@project = Project.includes([:user]).where( {:hashed_id=>params[:id]} ).first
end
В приложении /views/projects/invite.html.erg <%= debug( @project ) %>
возвращается:
--- !ruby/object:Project
attributes:
id: 22
name: Some Project Name
belongs_to: 1
instructions: Bla bla bla
active: true
max_duration: 2
max_videos:
created_at: 2013-08-26 15:56:50.000000000 Z
updated_at: 2013-08-26 15:56:50.000000000 Z
hashed_id: '1377532589'
Не следует ли включить в него связанный хэш/массив пользователя? Я знаю, что могу добавить его вручную, вызвав второй find
/where
(@project.user = User.where( {:id=>@project.belongs_to}
), но это не похоже на "The Rails Way". Что такое?
Решение
Мой первоначальный вопрос был сформулирован под неправильным предположением о том, что debug()
будет возвращать связанные объекты (это работает в cakePHP, потому что он объединяет все в массивы).
Итак, мой исходный код должен работать. Однако я неправильно назвал внешний ключ, указанный в таблице. Я запутался, посмотрев на метод миграции t.belongs_to
(который автоматически создает правильно названное поле foreign_key, а не поле с именем "принадлежит" ). Поэтому мне также пришлось переименовать этот столбец в user_id
, и теперь он работает так, как описано в ответе @Veraticus ниже.
Ответы
Ответ 1
Объект user
не является частью объекта project
, поэтому вы не сможете его просмотреть в проекте: скорее, говоря Project.includes(:user)
, вы сообщаете Rails, чтобы он загружал если он находит проект. Это экономит вам вызов базы данных по дороге. Например, не нетерпеливо:
@project = Project.where(id: params[:id]).first # one database call, fetching the project
@project.user # another database call, fetching the user
И с нетерпением:
@project = Project.includes(:user).where(id: params[:id]).first # one database call, fetching both project and user
@project.user # no database interaction
Это больше связано с запросами has_many
, где ассоциации с поддержкой загрузки могут сохранять запросы базы данных N + 1.
Вы можете убедиться, что это работает надлежащим образом, вызвав @project.user
в какой-то момент после интенсивной загрузки и проверки ваших журналов: вы должны увидеть, что в этот момент не было вызова базы данных.
Ответ 2
Желательная загрузка, оптимизация запросов N + 1 - действительно эффективный способ загрузки ассоциаций в один вызов.
- включает(), где() и find()
@project = Project.includes(:user).where(hashed_id: params[:id]).first
@project = Project.where(hashed_id: params[:id]).includes(:user).first
* В некоторых случаях может быть полезно *
@projects = Project.find(:all, :includes => :user)
@projects = Project.find(:all, :include => [{:association1 => [:associationA, :associationB, ....]}]