2015-08-25 70 views
1

我有一個模型Parent有很多孩子Child。我想要獲得所有父級模型,並顯示父級的每個Child。據我所知,這是Rails的includes方法的經典用例。使用Rails包括對兒童的條件

但是,我無法讓Rails向子模型添加條件,而不將父模型限制爲有子模型的子模型。

例如,這只是輸出父母有孩子:

Parent.includes(:children).where(children: {age: 10}).each do |parent| 
    # output parent info 
    parent.children.where("age = 10").each do |child| 
    #output child info 
    end 
end 

我看了Rails includes with conditions但似乎我有同樣的問題作爲問題的任擇議定書和接受的答案既不一部分並沒有解決它(它只有一些父母,或採用多個查詢)。

+0

什麼數據庫管理系統使用的是? –

+0

我使用MySQL – you786

回答

4

您需要使用LEFT JOIN

Parent.joins("LEFT JOIN children ON parent.id = children.parent_id") 
     .where("parent.age = 10 AND children.age = 10") 
     .select("parent.*, children.*") 

如果你想選擇可能會或可能不會有在children表中的相應行parent錶行,您使用LEFT JOIN條款。如果children表中沒有匹配的行,則children表中的列值將被替換爲NULL值。

0

沒有孩子誰都會有NULL的children.age的父母,你只濾波children.age = 10

嘗試

where('children.age = 10 or children.age is null') 
+0

號。不正確的。它並非總是如此。 –

2

這是包含方法的侷限性。你需要的是一個外部聯接,不幸的是,rails沒有一個強制外部聯接而不使用原始sql語法(#joins默認爲內部聯接和#includes緊急加載)的好方法。
嘗試使用的東西沿着

Parent.joins('LEFT OUTER JOIN child on child.parent_id = parent.id').where(...) 

此行應抓住所有的父母,甚至那些沒有孩子

+0

謝謝。如果我加入JOIN,我還能獲得性能優勢嗎?看起來我會 - 但是我不明白爲什麼'includes'甚至存在。 – you786

+0

@ you786'包括'不要加入。請參閱[這裏](http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations)。它通過使用'IN'查詢來加載。 –

+0

@ you766包括使用上面的調用風格在rails 4中進行聯接。它也默認爲一個LEFT OUTER JOIN,這就是你正在尋找的。如果你在你的語句中調用.to_sql,你會發現情況是這樣的,在rails 4中也是如此。Rails 3的工作方式不同,並使用內部連接。 – dinomix

1

這不是一個100%的答案,但一個方法是接受你西港島線讓所有的孩子記錄由熱切加載返回,但要選擇那些你希望使用非ActiveRecord方法的。

你會包括在預先加載比你需要更多的子記錄,所以這不是一個完美的解決方案效率較低,但你仍然可以得到你想要的記錄:

Parent.includes(:children).each do |parent| 
    parent.children.select{|child| child.age == 10}.each do |child| 
    blah blah... 
    end 
end 

我假設在這裏,你在您的選擇標準上需要很大的靈活性,並且基於範圍的關聯不會提供這種靈活性。

0

我遇到了這個問題,從而絆倒了這個問題。可悲的是,迄今沒有任何答案是解決方案。很高興,我找到了解決方案!部分歸功於文檔:) http://apidock.com/rails/ActiveRecord/QueryMethods/includes

正如文檔所建議的,只需包含關聯,然後向其添加條件是不夠的;你還必須「參考」協會references(:children)

現在,您還可以看到,我正在使用一些語法糖,我建議您在條件中合併,而不是重新寫入它們。如果可能,請使用它。

Parent.includes(:children).merge(Child.at_school).references(:children).first 

所以我做了什麼,以及我建議做的是建立了這樣的範圍:

class Parent < ActiveRecord::Model 

    has_many :children 

    scope :with_children_at_school, -> { includes(:children).merge(Child.at_school).references(:children) } 
    # ... 
end 

然後你就可以直接調用Parent.with_children_at_school.first(或者你想鏈上的其他任何結束!

我希望這有助於!