Rails includes with conditions

According to this guide Active Record Querying

You can specify conditions on includes for eager loading like this

Person.includes(:notes).where("notes.important", true)

It recommends to use joins anyway.

A workaround for this would be to create another association like this

class Person < ActiveRecord::Base
  has_many :important_notes, :class_name => 'Note', 
           :conditions => ['important = ?', true]
end

You would then be able to do this

Person.find(:all, include: :important_notes)

Rails 5+ syntax:

Person.includes(:notes).where(notes: {important: true})

Nested:

Person.includes(notes: [:grades]).where(notes: {important: true, grades: {important: true})

Rails 4.2+:

Option A - "preload": multiple selects, uses "id IN (...)"

class Person < ActiveRecord::Base
  has_many :notes
  has_many :important_notes, -> { where(important: true) }, class_name: "Note"
end

Person.preload(:important_notes)

SQL:

SELECT "people".* FROM "people"

SELECT "notes".* FROM "notes" WHERE "notes"."important" = ? AND "notes"."person_id" IN (1, 2)

Option B - "eager_load": one huge select, uses "LEFT JOIN"

class Person < ActiveRecord::Base
  has_many :notes
  has_many :important_notes, -> { where(important: true) }, class_name: "Note"
end

Person.eager_load(:important_notes)

SQL:

SELECT "people"."id" AS t0_r0, "people"."name" AS t0_r1, "people"."created_at" AS t0_r2, "people"."updated_at" AS t0_r3, "notes"."id" AS t1_r0, "notes"."person_id" AS t1_r1, "notes"."important" AS t1_r2 
FROM "people" 
LEFT OUTER JOIN "notes" ON "notes"."person_id" = "people"."id" AND "notes"."important" = ?

Same was discussed in Japanese stackoverflow. Quite hacky, but following seems to work, at least on rails 5.

Person.eager_load(:notes).joins("AND notes.important = 't'")

One important aspect is that by this way, you can write arbitrary join condition. Down side is that you cannot use placeholder so you need to be careful when using params as the join condition.

https://ja.stackoverflow.com/q/22812/754