ruby on rails - joins - Rails 4: Cómo usar includes() con where() para recuperar objetos asociados
rails joins includes (2)
No puedo averiguar cómo usar el método .where()
para recuperar los datos del modelo asociado. En este ejemplo, Projects pertenece a los usuarios ...
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
end
En App / views / projects / invite.html.erg <%= debug( @project ) %>
returns:
--- !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''
¿No debería incluirse el hash / array del usuario asociado en esto? Sé que podría agregarlo manualmente llamando a un segundo find
/ where
( @project.user = User.where( {:id=>@project.belongs_to}
) pero esto no se siente como "The Rails Way". ?
Solución Mi pregunta inicial se formuló bajo la suposición incorrecta de que debug()
devolvería los objetos asociados (esto funciona en CakePHP porque agrupa todo en matrices).
Entonces mi código original debería funcionar. Sin embargo, he nombrado incorrectamente la clave externa archivada en la tabla. Me confundí al mirar el método de migración t.belongs_to
(que crea automáticamente el campo foreign_key correctamente nombrado, no un campo llamado "belongs_to"). Así que también tuve que cambiar el nombre de esa columna a user_id
y ahora funciona tal como se describe en la respuesta de @ Veraticus a continuación.
Ansiosa carga, la optimización de consultas N + 1 es realmente una forma eficiente de cargar asociaciones en una sola llamada.
- includes () con where () y find ()
@project = Project.includes(:user).where(hashed_id: params[:id]).first
@project = Project.where(hashed_id: params[:id]).includes(:user).first
* En algunos casos, puede ser útil *
@projects = Project.find(:all, :includes => :user)
@projects = Project.find(:all, :include => [{:association1 => [:associationA, :associationB, ....]}]
El objeto del user
no es parte del objeto del project
, por lo que no podrá verlo en el proyecto: más bien, al decir Project.includes(:user)
, le está diciendo a Rails que cargue ansiosamente la asociación a la que se hace referencia cuando encuentra el proyecto. Esto le ahorra una llamada a la base de datos en el camino. Por ejemplo, sin entusiasmo:
@project = Project.where(id: params[:id]).first # one database call, fetching the project
@project.user # another database call, fetching the user
Y con entusiasmo
@project = Project.includes(:user).where(id: params[:id]).first # one database call, fetching both project and user
@project.user # no database interaction
Esto es más importante con has_many
consultas donde las asociaciones de carga ansiosa pueden guardar consultas de bases de datos N + 1.
Puede verificar que esto está funcionando adecuadamente llamando a @project.user
en algún momento después de la carga ansiosa y verificando sus registros: debería ver que no hubo una llamada a la base de datos en ese momento.