Reputation: 5807
I'm basically trying to do something like this:
class A < ActiveRecord::Base
has_many :bs
end
class B < ActiveRecord::Base
belongs_to :a
has_many :cs
end
class C < ActiveRecord::Base
belongs_to :b
has_many :ds
end
class D < ActiveRecord::Base
...
# and so on with class E, F, G, ...
# get all `C` for all `B` of `A` does not work
A.first.bs.cs
--> undefined method `cs' for #<ActiveRecord::Associations::CollectionProxy::ActiveRecord_Associations_CollectionProxy_B:0xxxxx>
My naive approach is to monkey patch array with a new function and us it like this:
class Array
def get (m)
self.map{|o| o.send(m)}.delete_if(&:empty?).flatten
end
end
# works:
A.first.bs.get(:cs)
# works too:
A.all.get(:bs).get(:cs)
# works:
A.all.get(:bs).get(:cs).get(:ds).get(:es)
Are there any pitfalls I do not see at the moment? Monkey patching Array
with a function like this smells for me a little bit - is there any cleaner approach?
I simply want to chain those has_many
-associations without much hassle in my code. Maybe there's already a gem for it I've not found yet?
Upvotes: 0
Views: 96
Reputation: 44675
First of all, object returned by your bs
method is not an array - it is much more complicated AssociationProxy
object, which wraps an array called internally target
. Your approach leads to extreme case of N+1
problem.
The correct approach is to introduce has_many :through
association:
class A < ActiveRecord::Base
has_many :bs
has_many :cs, through: :bs
end
Then you can just call:
A.cs
Without any changes in db.
If you had more nested association (let's say C has_many :ds), you can get them through another has_many :through association:
class A < ActiveRecord::Base
has_many :bs
has_many :cs, through: :bs
has_many :ds, through: :cs
end
Upvotes: 2