Ruby 在 Rails - 使用 HAVING 覆盖范围的 JOIN

Ruby On Rails - Coverting a JOIN using HAVING to a scope

我之前试着问过这个问题,但并不顺利 - 希望这次我做得更好。

我有三个模型

class Flavor < ActiveRecord::Base
  has_many :components
  has_many :ingredients, through: :components
end

class Ingredient < ActiveRecord::Base
  has_many :components
  has_many :flavors, through: :components
end

class Component < ActiveRecord::Base
  belongs_to :ingredient
  belongs_to :flavor

  validates :percentage, presence: true 
end

批次是由口味组成的,但只有当口味的成分加起来达到 100% 时,才能将其制成批次(因此我将百分比验证放在那里以表示它的原因)。

起初我试着把它写成一个范围,但永远无法让它工作,我创建的模型测试工作使用

def self.batch_eligible
  self.find_by_sql("Select flavors.* FROM flavors 
  INNER JOIN components on flavors.id = components.flavor_id 
  GROUP BY flavors.id, flavors.name 
  HAVING SUM(percentage)=100")
end

我确实尝试过瞄准镜但失败了。这是我想出的范围的最终版本:

scope :batch_eligible, -> {joins(:components).having('SUM(percentage) = 100').group('flavor.id')}

生成的对象将用于以批处理的形式填充选择列表(风味可以在组件完全计算出来之前存在)。

我认为这里的限制是我对作用域的理解 - 那么如何正确构建作用域以产生与 find_by_sql 表达式相同的结果?

感谢所有帮助,谢谢。

回应第一条评论 - 我尝试了各种范围但没有捕获错误 - 以上范围 returns 这个错误:

 ActiveRecord::StatementInvalid:
   PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "flavor"
   LINE 1: SELECT COUNT(*) AS count_all, flavor.id AS flavor_id FROM "f...
                                         ^
   : SELECT COUNT(*) AS count_all, flavor.id AS flavor_id FROM "flavors" INNER JOIN "components" ON "components"."flavor_id" = "flavors"."id" GROUP BY flavor.id HAVING SUM(percentage) = 100

将其更改为 flavors id 使其成为 'work' 但 return 不是正确的信息。

再来一段代码——模型测试

require 'rails_helper'

RSpec.describe Flavor, type: :model do
  let!(:flavor) {FactoryGirl.create(:flavor)}
  let!(:flavor2) {FactoryGirl.create(:flavor)}
  let!(:ingredient) {FactoryGirl.create(:ingredient)}
  let!(:component) {FactoryGirl.create(:component, flavor: flavor, ingredient: ingredient, percentage: 25)}
  let!(:component1) {FactoryGirl.create(:component, flavor: flavor2, ingredient: ingredient, percentage: 100)}

  it "should have a default archive as false" do
    expect(flavor.archive).to be(false)
  end

  it "should only have valid flavors for batch creation" do
    expect(Flavor.batch_eligible.count).to eq 1
    expect(Flavor.batcH_eligible.first).to eq flavor2
  end
end

即使使用干净的测试数据库 - batch_eligible 计数是 4 - 不是一个

请注意 - 使用 find_by_sql 函数时测试确实通过了 - 我只是认为范围应该是可能的?

向@taryneast 寻求帮助的道具 - 我被这个指出了正确的方向。

使用 flavors.id 更正范围问题后 - 我做了 运行 检查以查看发生了什么,但我也 运行 各种功能。

puts Flavor.batch_eligible.countputs Flavor.batch_eligible.size 都产生相同的结果,例如,散列 {312 => 1} - 312 将是 Factory Created Flavor 的 ID。

所以问题(一旦我解决了 flavors.id)不在范围内 - 它在测试中。您需要测试长度,Flavor.batch_eligible.length 产生我想要的整数 1。

也许其他人都知道 - 我不知道。

谢谢塔林