问题
I can't figure out how to user the .where()
method to retrieve associated model data. In this example, Projects belongs_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
end
In 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'
Shouldn't the associated User hash/array be included in this? I know I could manually add it by calling a second find
/where
( @project.user = User.where( {:id=>@project.belongs_to}
) but this doesn't feel like "The Rails Way". What is?
Solution
My initial question was formulated under the incorrect assumption that debug()
would return associated objects (this works in cakePHP because it bundles everything into arrays).
So my original code should work. However, I had incorrectly named the foreign key filed in the table. I got confused by looking at the migration method t.belongs_to
(which automatically creates the correctly named foreign_key field, not a field named "belongs_to"). So I also had to rename that column to user_id
and now it works just as described in @Veraticus's answer below.
回答1:
The user
object is not part of the project
object, so you won't be able to view it on the project: rather, by saying Project.includes(:user)
, you're telling Rails to eager-load the referenced association when it finds the project. This saves you a database call down the road. For example, non-eagerly:
@project = Project.where(id: params[:id]).first # one database call, fetching the project
@project.user # another database call, fetching the user
And eagerly:
@project = Project.includes(:user).where(id: params[:id]).first # one database call, fetching both project and user
@project.user # no database interaction
This matters more with has_many
queries where eager-loading associations can save N+1 database queries.
You can verify this is working appropriately by calling @project.user
at some point after the eager load and checking your logs: you should see that there was no database call at that point.
回答2:
Eager loading, N+1 query optimization is really an efficient way of loading associations in a single call.
- includes() with where() and find()
@project = Project.includes(:user).where(hashed_id: params[:id]).first
@project = Project.where(hashed_id: params[:id]).includes(:user).first
* In some cases, It can be useful*
@projects = Project.find(:all, :includes => :user)
@projects = Project.find(:all, :include => [{:association1 => [:associationA, :associationB, ....]}]
来源:https://stackoverflow.com/questions/18449209/rails-4-how-to-use-includes-with-where-to-retrieve-associated-objects