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