23

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.

Carl Edwards
  • 13,826
  • 11
  • 57
  • 119
emersonthis
  • 32,822
  • 59
  • 210
  • 375
  • What exactly are you trying to show? Do you mean that you want debug output showing the @project.user? Or are you saying that your `where` and `includes` aren't working? If you want to show the `user` association for debug, did you try `<%= debug(@project.user) %>` without doing the extra `user.where...`? – lurker Aug 26 '13 at 17:10
  • if you have just one project object includes does not make sense to me. includes makes sense when several projects are selected and you want to load all respective users in just one query, is that what you were doing? or how are you using `includes`, I hope you remember – sites Jul 31 '14 at 21:24
  • The answers are no longer producing single query, but this approach does: http://blog.arkency.com/2013/12/rails4-preloading/ – hakunin Apr 29 '15 at 15:09

2 Answers2

39

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.

Veraticus
  • 15,944
  • 3
  • 41
  • 45
  • Huh. So it *is* working, I just don't know it because I'm (incorrectly) expecting `debug` to return the associations. Makes sense. However, when I `<%= debug( @project.user ) %> I get `---` (nothing).` Any ideas? – emersonthis Aug 26 '13 at 17:32
  • Your project probably has no user, is my guess. – Veraticus Aug 26 '13 at 17:48
  • Not quite sure what you mean, but I'm pretty sure it does. My models are defined (see above) and I have one row in my users table. Am I missing something? – emersonthis Aug 26 '13 at 17:51
  • Yes, if your project's `user_id` attribute is empty then there's no way for it to associate it with that user. You might want to check out the [Rails guide to association basics](http://guides.rubyonrails.org/association_basics.html) to understand how to set up associations properly. – Veraticus Aug 26 '13 at 17:54
  • Just to clarify, the one user record has a `belongs_to` value of 1, which I think is the Rails 4 convention for foreign keys. Right? – emersonthis Aug 26 '13 at 17:54
  • Ohh... the field name should be `user_id`? That's how I originally had it but I saw it written as belongs_to and changed it. – emersonthis Aug 26 '13 at 17:55
  • Yup. That was it. I got confused by looking at the migration method `t.belongs_to` (which creates the correctly named foreign_key field, *not* a field named "belongs_to") – emersonthis Aug 26 '13 at 18:00
  • Anyone know why you cannot use `includes` with `find`? Seems like you have to do `Model.includes(:other_model).where(id: one_id).first` instead... – mecampbellsoup May 03 '17 at 19:05
  • Now what if you wanted to do `@project.user.where(active: true)` Rails does not seem smart enough to not issue another SQL query. – Ray Suelzer Jun 12 '19 at 23:07
7

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, ....]}]
przbadu
  • 5,769
  • 5
  • 42
  • 67