2013-10-15 78 views
0

考慮以下作用域通過協會

class Engineer < ActiveRecord::Base 
    has_many :ideas 
    scope :masters, -> { where(educational_level: ["master", "candidate"]) } 
end 

class Idea < ActiveRecord::Base 
    belongs_to :engineer 
    scope :popular, -> { where("rating > ?", 4) } 
end 

現在,讓我們說,我想找回由工程師age下令主工程師設想所有流行的想法。爲簡單起見,假設age只是engineers表中的整數列。

這樣做的一種方法是先取工程師。

popular_ideas_by_educated_engineers = [] 
Engineer.masters.order("age").each do |engineer| 
    popular_ideas_by_educated_engineers += engineer.ideas.popular 
end 

該代碼具有n + 1分的問題,因爲想法並不急於加載,它的整體感覺笨重。

我還想寫的方式代碼,允許通過編寫類似

Idea.popular.by_masters.order_by_engineer_age 

來得到想要的結果這是可能的 - 如果是這樣,怎麼樣?

我知道我可以硬編碼SQL到Idea類聯接上engineers,並規定了一些限制,但我所尋找的是利用在Engineer類已經定義的範圍。

回答

1

你可能想嘗試和翻轉過來,這樣,像你這樣的建議:

Idea.popular.includes(:engineer).by_masters.order('engineers.age') 

如果您by_masters範圍看起來像:

scope :by_masters, -> { where: { engineers: { educational_level: 'master' } } } 

這是includes呼叫強制JOIN,使這可能。

+0

是啊,我已經試過類似的東西,但這種亂丟有關工程師類知識理念類,比如這段代碼顯示的表名,二稱爲educational_level和age的屬性以及educational_level屬性的約束。如果這可以隱藏起來會很好,所以Idea只使用範圍,因此避免了重複。 –

+0

儘管隱藏着隱藏的東西是正確的想法,但Rails和ActiveRecord特別會在每一步都與您相抗衡。也許你可以找到一種方法來做到這一點沒有衝突的擔憂,但我認爲最好的方法是順其自然。 – tadman

0

您可以使用連接和合並在這裏:

class Engineer < ActiveRecord::Base 
    scope :masters, -> { where(educational_level: ["master", "candidate"]) } 
    scope :ordered_by_age, -> { order(:age) } 
end 

class Idea < ActiveRecord::Base 
    belongs_to :engineer 

    scope :popular, -> { where("rating > ?", 4) } 
    scope :by_master, -> { joins(:engineer).merge(Engineer.masters) } 
    scope :order_by_engineer_age, -> { joins(:engineer).merge(Engineer.order_by_age) } 
end