2

So, I'm a Rails newbie and may be missing something obvious, but I'm a bit flabbergasted as to why I'm getting a NoMethodError when I attempt to eager load an association using .includes.

Here's my code:

forecasts_controller.rb

def show
    @forecast = Forecast.find(params[:id])      
    @forecast_projects = Project.includes(:project_type).where("forecast_id =?", params[:id])
end

_project.html.erb (this file is a collection partial rendered in the Forecast action)

<%= project.project_type.title %>

For some reason, this produces the following error:

NoMethodError in Forecasts#show

Showing /path where line #1 raised:

undefined method `title' for nil:NilClass

Oddly enough, if I change forecasts_controller.rb to...

def show
    @forecast = Forecast.find(params[:id])      
    @forecast_projects = Project.joins(:project_type).where("forecast_id =?", params[:id])
end

Suddenly everything starts working perfectly. Can someone help me figure out what I'm missing here (and excuse my lack of experience)?

MrYoshiji
  • 54,334
  • 13
  • 124
  • 117
PJ McCormick
  • 1,903
  • 14
  • 12

2 Answers2

3

Your code does not work because there is one (or more) project having no project_type associated.

The big difference between joins and includes:

  • joins retrieve only the records that have at least 1 association
  • includes retrieve the records and load their association if it exists

Rails :include vs. :joins

That means when you call:

@forecast_projects = Project.includes(:project_type).where("forecast_id =?", params[:id])
# loads all the projects AND their associated project_type if exists
@forecast_projects = Project.joins(:project_type).where("forecast_id =?", params[:id])
# loads all the projects HAVING at least 1 project_type associated

To clearly see the difference, try this:

Project.includes(:project_type).count # total number of entries in projects table
Project.joins(:project_type).count # number of projects having at least 1 project_type

To fix your problem, you can try to display the title of the associated project_type if it exists:

<%= project.project_type.try(:title) %>
# will print the project_type.title if exists, prints nothing if project_type.nil?
<%= project.project_type.try(:title) || 'No ProjectType for this Project' %>
# will print the string if project_type is nil
Community
  • 1
  • 1
MrYoshiji
  • 54,334
  • 13
  • 124
  • 117
  • 1
    Thanks! That was super helpful. I appreciated the explanation of the core problem, as well as the additional introduction to the try method. – PJ McCormick May 22 '13 at 22:18
1

You have at least one project without project_type. When you use includes rails selects all records and fails on the one without project_type. When you use joins it selects only those with project_type, but doesn't include them.

To fix the problem you better find the bad record and add the project_type to it and add validation to avoid this error in future. But if it's ok to have projects with blank project_type then use both joins and includes like Project.joins(:project_type).includes(:project_type).

Gacha
  • 1,037
  • 8
  • 18