【问题标题】:Ruby On Rails - Coverting a JOIN using HAVING to a scopeRuby On Rails - 使用 HAVING 将 JOIN 覆盖到范围
【发布时间】:2017-02-13 07:32:14
【问题描述】:

我之前尝试过问这个问题,但效果不佳 - 希望这次我能做得更好。

我有三个模型

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, -&gt; {joins(:components).having('SUM(percentage) = 100').group('flavor.id')}

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

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

感谢所有帮助,谢谢。

响应第一条评论 - 我尝试了各种范围但没有捕获错误 - 上面的范围返回此错误:

 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

将其更改为风味 id 使其“工作”,但它不会返回正确的信息。

再一段代码——模型测试

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 请参阅上面的编辑 - 关于错误 - 我如何尝试修复它以响应错误但仍然无法正常工作。我真的认为这将是一件容易的事情
  • 好的,所以回复:抱怨 flavor_id 的事情...您可能需要明确说明这些列属于什么,例如也许可以尝试 joins(:components).references(:components).having('SUM(components.percentage) = 100').group('components.flavor_id') 我也建议不要从 count 开始-> 有时复杂的作用域可以工作,但 Rails 无法将其转换为正确的“计数”查询......首先让它返回 .all,然后在其上执行 .size(只是为了让它部分工作)。 ..然后弄清楚如何让count正常工作。
  • 另外 - 而不是测试计数...尝试puts找出各种口味,包括总和百分比,看看你得到了什么以及它与你期望的 1 有何不同?最后,使用to_sql 查看它试图生成的内容,例如:Flavor.batch_eligible.to_sql
  • 通常当 rails 构建一个 SQl 查询时,它会为每个 db-table 提供一个简短的代号(例如 t3)...但是如果您要在后面的方法之一(例如,我们使用components.flavour_id)然后你需要告诉rails你将要“稍后引用表名”......即references :)

标签: ruby-on-rails activerecord named-scope


【解决方案1】:

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

在使用 flavor.id 更正范围问题后 - 我确实运行了检查以查看发生了什么,但我也运行了各种函数。

puts Flavor.batch_eligible.countputs Flavor.batch_eligible.size 都产生相同的结果,例如,哈希 {312 => 1} - 312 将是工厂创建风味的 id。

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

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

谢谢塔林

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-19
    • 2012-05-01
    • 2017-06-03
    相关资源
    最近更新 更多