2014-01-15 2 views
0

Я в основном пытаются сделать что-то вроде этого: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 -соединения без лишних хлопот в моем коде. Может быть, у вас уже есть драгоценный камень, которого я еще не нашел?

+2

Может быть 'has_many: CS, через:: bs'? –

+0

Почему вы не проверяете has_many: через опцию –

+0

ясно сказано, что он может идти вниз, а не только на два уровня, поэтому я думаю, что 'through' не работает для перехода от A к E? – Markus

ответ

2

Прежде всего, объект, возвращаемый вашим методом bs, не является массивом - гораздо сложнее AssociationProxy объект, который обертывает массив, называемый внутренним target. Ваш подход приводит к крайнему случаю проблемы N+1.

Правильный подход заключается в ознакомлении has_many :through ассоциацию:

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

Тогда вы можете просто позвонить:

A.cs 

без каких-либо изменений в БД.

Если вы имели более вложенную ассоциацию (скажем, C HAS_MANY: DS), вы можете получить их через другой has_many: через ассоциацию:

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

Да, забыл упомянуть 'N + 1' :) –

+0

Что делать, если есть больше аськов? C has_many D, D has_many E и т. Д. Как добраться от A до E? – Markus

+0

Обновленный ответ. :) – BroiSatse

Смежные вопросы