activerecord chained has_many finder

时间:2014-01-15 11:20:21

标签: ruby activerecord has-many

我基本上是想做这样的事情:

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>

我天真的方法是使用新功能来修补补丁数组,我们就像这样:

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)

目前我还没有看到任何陷阱吗?用这样的函数修补Array的猴子对我来说有点味道 - 有没有更干净的方法?

我只是想在我的代码中没有太多麻烦地链接那些has_many - 关联。也许已经有了它的宝石我还没找到?

1 个答案:

答案 0 :(得分:2)

首先,bs方法返回的对象不是数组 - 它是一个更复杂的AssociationProxy对象,它包含一个名为internal target的数组。您的方法会导致N+1问题的极端情况。

正确的方法是引入has_many :through关联:

class A < ActiveRecord::Base
  has_many :bs
  has_many :cs, through: :bs 
end

然后你可以打电话:

A.cs

db中没有任何变化。

如果你有更多的嵌套关联(让我们说C has_many:ds),你可以通过另一个has_many来获取它们:通过关联:

class A < ActiveRecord::Base
  has_many :bs
  has_many :cs, through: :bs
  has_many :ds, through: :cs
end