Rails ActiveRecord 模型范围与 has_many 关联的连接

Rails ActiveRecord model scope with joins on has_many associations

我目前正在我的 Rails 模型中设置一个范围,供 ActiveAdmin 使用。我要构建的范围应该找到每个 Job 过去有 survey_date,现在有 Job.survey,没有 Job.quotes

这是我的 Job 模型的简化版本:

has_many :quotes
has_many :surveys

scope :awaiting_quote, lambda { joins(:surveys, :quotes).where('survey_date < :current_time AND surveys.id IS NOT NULL AND quotes.id IS NULL', { current_time: Time.current }) }

我应该如何更改我的范围以使其正确找到相关的 Job 记录?

更新 Rails 5

mad_raz mentions, in Rails 5.0+, you can use left_outer_joins:

scope :awaiting_quote, -> { joins(:surveys).
  left_outer_joins(:quotes).
  where('survey_date < :current_time', { current_time: Time.current }).
  where('quotes.id IS NULL')
}

但是,您仍然必须仅对 return 那些尚未收到报价的记录提供 where('quotes.id IS NULL') 检查。请参阅 以直观地了解外部联接。

将它们分成两个单独的范围可能仍然是最有意义的。


Rails 4

您可以使用 joins 创建左外部联接,您只需要更明确一点。

scope :awaiting_quote, -> { joins(:surveys).
  joins('LEFT OUTER JOIN quotes ON quotes.job_id = jobs.id').
  where('survey_date < :current_time', { current_time: Time.current }).
  where('quotes.id IS NULL')
}

您不需要 surveys.id IS NOT NULL,因为成功的内部联接不会包含 nil id。

将它们分成两个单独的范围可能更有意义,:has_survey:without_quote,然后可以将它们组合成一个方法。

def self.awaiting_quote
  Job.has_survey.without_quote
end

Rails5介绍left_outer_joins可以使用的方法

scope :awaiting_quote, -> { joins(:surveys).left_outer_joins(:quotes).where('yada yada') }