代码之家  ›  专栏  ›  技术社区  ›  bo-oz

是否可以创建或加入Rails以获取与两个不同模型相交的记录?

  •  1
  • bo-oz  · 技术社区  · 8 年前

    我还发布了一个相关问题: https://stackoverflow.com/a/50613524?noredirect=1 ,更多的是关于这个概念。

    我想做的是:我想找到 Accounts 这是一个特定的原因 User 创造了一个 Report , 用户有一个 deadline .

      scope :active_accounts_for_user, -> (user) {
        joins(:deadlines)
        .where(:deadlines => {user: [nil, user]})
        .joins(:reports)
        .where(:reports => {user: user, day: (Date.today - 1.day..Date.today)})
      }
    

    问题是Rails创建了一个AND查询,所以它只能找到既有报告又有截止日期的帐户。对于我的用例来说,这显然是限制性的。

    编辑:好的,我找到了一个解决方案,我想确认一下这是否是一个很好的练习。我基本上是对这两个模型创建单独的查询,以获取帐户ID,我在一个简单的示例中使用它:

      scope :active_accounts_for_user, -> (user) {
        accounts_with_reports = Report.where(user: user).where('date > ?', 24.hours.ago).pluck(:account_id)
        accounts_with_deadlines = Deadline.where(user: user).pluck(:account_id)
        account_ids = (accounts_with_reports + accounts_with_deadlines).compact
        return includes(:past_deadlines, :deadlines).where(id: account_ids)
      }
    

    这是示波器的正确用法吗?

    2 回复  |  直到 8 年前
        1
  •  1
  •   Phil    8 年前

    一种选择是使用普通SQL。

    如果我正确理解了您的模式,那么您所追求的是需要左连接而不是OR运算符的东西。内部联接上的OR运算符将无法实现您希望的结果,因为任何只有截止日期但没有报告的记录都不会被返回。相反,LEFT JOIN会返回截止日期或报表表中有匹配用户id的所有帐户记录。

    select * from accounts 
    left join deadlines on deadlines.user = accounts.user
    left join reports on reports.user = accounts.user
    where day ...
    and (reports.user IS NOT NULL or deadline.user IS NOT NULL)
    

    在Rails 4中,我相信您可以为连接指定SQL代码,同时像通常一样链接where子句。因此,这将成为:

    joinsql = "left join deadlines on 
     deadlines.user = accounts.user
     left join reports 
     on reports.user = accounts.user"
    
    Account.join(joinsql).where(
       accounts: {user: user}, 
       deadline: {day: (Date.today - 1.day..Date.today)}
     ).where("reports.user IS NOT NULL or deadlines.user IS NOT NULL")
    

    然后可以访问所有联接表的结果。

    注意,添加了链接到第一个where子句的第二个where子句,有效地避免了纯文本条件,以确保报告和截止日期都不是空的。

    如果通过“用户”属性将表连接在一起,则可以使用普通活动记录查询来使用

    Account.includes(:accounts).includes(:deadlines).where... 
    

    见: http://guides.rubyonrails.org/active_record_querying.html#specifying-conditions-on-eager-loaded-associations

    另一种避免左联接的方法是,对一个表使用内部联接,对另一个表使用具有内部联接的类似查询进行并集。为此,必须使用 .select 确保工会能够运作。在我看来,这更棘手。

    不管是哪种方式,编写包含用户条目的where子句条件的SQL通常都比较容易,而不是猜测Rails将如何创建所需的查询。

        2
  •  1
  •   maicher    8 年前

    这在Rails 5.0及更高版本中是可能的。

    指南中有一节介绍了如何做到这一点: http://guides.rubyonrails.org/active_record_querying.html#or-conditions

    唯一的 接住 对于连接,它们需要在两个关系中复制,以使整个查询在结构上兼容:

    scope :active_accounts_for_user, -> (user) {
     joins(:deadlines).joins(:reports).where(deadlines: ...).or(
       joins(:deadlines).joins(:reports).where(reports: ...)
      )
    }