【问题标题】:Using Rails includes with conditions on children使用 Rails 包括对子项的条件
【发布时间】:2015-08-25 15:28:07
【问题描述】:

我有一个模特Parent 有很多孩子Child。我想获取所有父模型并显示父的每个Child。据我所知,这是 Rails 的 includes 方法的经典用例。

但是,如果不将父模型限制为具有子模型的模型,我无法让 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,但似乎我遇到了与问题的 OP 相同的问题,并且接受的答案都没有解决它(它要么只有一些父母,要么诉诸多个查询)。

【问题讨论】:

  • 您使用的是什么 DBMS?

标签: ruby-on-rails rails-activerecord


【解决方案1】:

您需要使用LEFT JOIN

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

如果要从parent 表中选择在children 表中可能有也可能没有对应行的行,请使用LEFT JOIN 子句。如果children 表中没有匹配的行,则children 表中的列值将替换为NULL 值。

【讨论】:

    【解决方案2】:

    这是包含方法的限制。您需要的是一个外连接,不幸的是,rails 没有一个很好的方法来强制外连接而不使用原始 sql 语法(#joins 默认为内连接,#includes 急切加载)。
    尝试使用类似于

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

    这应该抓住所有的父母,即使是那些没有孩子的人

    【讨论】:

    • 谢谢。如果我进行 JOIN,我还能获得性能优势吗?似乎我会 - 但后来我不明白为什么includes 甚至存在。
    • @you786 includes 不要加入。见here。它通过使用IN 查询进行急切加载。
    • @you766 Includes 确实使用上面的调用样式在 rails 4 中加入。它还默认为您正在寻找的 LEFT OUTER JOIN。如果您在语句中调用 .to_sql,您会看到这种情况,在 rails 4 中也是如此。Rails 3 的工作方式不同,并使用内部连接。
    【解决方案3】:

    我遇到了这个问题,因此偶然发现了这个问题。遗憾的是,到目前为止,没有一个答案是解决方案。很高兴,我找到了解决方案!部分感谢文档 :) 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(或者任何你想链接到最后的东西!

    我希望这会有所帮助!

    【讨论】:

    • 这与 Parent.includes(:children).where(...) 的作用相同,即仅限于有孩子的父母。
    【解决方案4】:

    这不是一个 100% 的答案,但一种方法是接受您将获得所有由急切加载返回的子记录,但使用非 ActiveRecord 方法选择您想要的那些。

    您将在预先加载中包含比您需要的更多的子记录,因此效率低于完美解决方案,但您仍然可以获得所需的记录:

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

    我在这里假设您在选择标准上需要很大的灵活性,而基于范围的关联不会提供这种灵活性。

    【讨论】:

      【解决方案5】:

      没有孩子的父母会有一个children.age为NULL,你只过滤children.age = 10。

      试试

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-25
        • 1970-01-01
        • 2021-09-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多