tl;dr – you need to specify the “inverse_of” option on your has_many/belongs_to relationship to get your children fully populated
So I came across an interesting situation the other day, where my includes() statement was preloading data, but then when accessing it I would see another SQL query.
require 'active_record' class Parent < ActiveRecord::Base attr_accessible :name has_many :parent_children, inverse_of: :children has_many :children, through: :parent_children default_scope includes(:parent_children) end class ParentChildren < ActiveRecord::Base attr_accessible :parent_id, :child_id belongs_to :parents, inverse_of: :parent_children belongs_to :children default_scope includes(:children) end class Child < ActiveRecord::Base attr_accessible :name has_many :parent_children has_many :parents, through: :parent_children end Parent.all.each {|p| p.children.each {|c| puts c.name}}
Without the :inverse_of here, each call to write the childs name will result in a new SQL query to get the data, even though this data has already been prefetched (eager loaded).