2015-08-25 42 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(或者你想链上的其他任何结束!

我希望这有助于!