First of all, you need to make a couple changes.
class C
needs an association to D
class C < ActiveRecord::Base
belongs_to :B
has_one :D
end
If you want to access A
's D
's, you need to specify this as well.
class A < ActiveRecord::Base
has_many :B
has_many :C, :through => :B
has_many :D, :through => :C
end
Now, to access all of A
's C
's:
-> a = A.where(:id => 1).includes(:C).first
A Load (0.2ms) SELECT "as".* FROM "as" WHERE "as"."id" = 1 LIMIT 1
B Load (0.1ms) SELECT "bs".* FROM "bs" WHERE "bs"."a_id" IN (1)
C Load (0.1ms) SELECT "cs".* FROM "cs" WHERE "cs"."b_id" IN (1, 2)
=> #<A id: 1, created_at: "2012-01-10 04:28:42", updated_at: "2012-01-10 04:28:42">
-> a.C
=> [#<C id: 1, b_id: 1, created_at: "2012-01-10 04:30:10", updated_at: "2012-01-10 04:30:10">, #<C id: 2, b_id: 1, created_at: "2012-01-10 04:30:11", updated_at: "2012-01-10 04:30:11">, #<C id: 3, b_id: 2, created_at: "2012-01-10 04:30:21", updated_at: "2012-01-10 04:30:21">, #<C id: 4, b_id: 2, created_at: "2012-01-10 04:30:21", updated_at: "2012-01-10 04:30:21">]
Notice how another query is not executed when you call a.C
. This is because ActiveRecord knows you will want to access the found A
's C
's by the include
call, and generates the minimum number of queries. Same goes for D
's:
-> a = A.where(:id => 1).includes(:D).first
A Load (0.1ms) SELECT "as".* FROM "as" WHERE "as"."id" = 1 LIMIT 1
B Load (0.1ms) SELECT "bs".* FROM "bs" WHERE "bs"."a_id" IN (1)
C Load (0.1ms) SELECT "cs".* FROM "cs" WHERE "cs"."b_id" IN (1, 2)
D Load (0.1ms) SELECT "ds".* FROM "ds" WHERE "ds"."c_id" IN (1, 2, 3, 4)
Say you wanted all A
's D
's but wanted C
's ordered:
A.where(:id => 1).includes(:C).order('cs.created_at DESC').includes(:D)
Note you can also set this as a default on the association:
The :order
option dictates the order in which associated objects will be received (in the syntax used by an SQL ORDER BY
clause).
class Customer < ActiveRecord::Base
has_many :orders, :order => "date_confirmed DESC"
end