Область действия Rails основана на сумме ассоциации has_many - PullRequest
0 голосов
/ 30 июня 2018
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 голосов
/ 01 июля 2018

TL; DR:

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

@ chiperific edit: Вышеприведенное выдало ошибку 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...