基于has_many关联之和的Rails范围

时间:2018-06-30 18:29:31

标签: ruby-on-rails

class Project
  has_many :tasks
  monetize :budget_cents # to create :budget
end

class Task
  belongs_to :project
  monetize :cost_cents # to create :cost
end

我想为Project.over_budget创建一个范围,该范围涉及对相关任务的成本求和。

在我看来,它看起来像这样:

scope :over_budget, ->{ where('budget_cents <= ?', tasks.map(&:cost_cents).sum) }

我尝试过的其他事情:

scope :over_budget, -> { joins(:tasks).where('budget <= ?', tasks.map(&:cost).sum) }

scope :over_budget, -> { |proj| joins(:tasks).where('budget <= ?', proj.tasks.map(&:cost).sum) }

scope :over_budget, -> { |proj| where('budget <= ?', Task.where('project_id=?', proj.id).map(&:cost).sum) }

scope :over_budget, -> { |proj| joins(:tasks).where('budget <= ?', Tasks.where('project_id=?', proj.id).map(&:cost).sum) }

这实际上返回没有错误,但是对于所有任务,而不是相关的:

scope :over_budget, ->{ where('budget_cents <= ?', Task.map(&:cost_cents).sum) }

我确定我缺少明显的东西,但是看不到。

1 个答案:

答案 0 :(得分:1)

TL; DR:

class Project < ApplicationRecord
  # ...
  scope :over_budget, -> { joins(:tasks).group(:id).having('sum(tasks.cost_cents) > budget_cents') }
end

@chiperific编辑: 上面引发了ambiguous错误,但是当我稍加修改后它就起作用了:

class Project < ApplicationRecord
  # ...
  scope :over_budget, -> { joins(:tasks).group(:id).having('sum(tasks.cost_cents) > projects.budget_cents') }
end

说明:

  • Project.joins(:tasks)的结果如下:

    | projects.id | projects.budget_cents | tasks.id | tasks.cost_cents |   
    | ----------- | --------------------- | -------- | ---------------- |
    | 1           | 5                     | 1        | 2                |
    | 1           | 5                     | 2        | 3                |
    | 1           | 5                     | 3        | 1                |
    | 2           | 10                    | 4        | 1                |
    
  • 然后,将.group(:id)附加到上面的Project.joins(:tasks)上,现在结果如下:

    | projects.id | projects.budget_cents | tasks.id | tasks.cost_cents |   
    | ----------- | --------------------- | -------- | ---------------- |
    | 1           | 5                     | 1        | 2                |
    | 2           | 10                    | 4        | 1                |
    
  • 然后,最后将.having('sum(tasks.cost_cents) > budget_cents')附加到Project.joins(:tasks).group(:id)上,现在结果类似于:

    | projects.id | projects.budget_cents | tasks.id | tasks.cost_cents | sum(tasks.costs_cents) |
    | ----------- | --------------------- | -------- | ---------------- | ---------------------- |
    | 1           | 5                     | 1        | 2                | 6
    
    • 请注意,上面的projects.id = 2行已不存在,因为其sum(tasks.costs_cents)仅等于1