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.count
或 puts Flavor.batch_eligible.size
都产生相同的结果,例如,散列 {312 => 1} - 312 将是 Factory Created Flavor 的 ID。
所以问题(一旦我解决了 flavors.id)不在范围内 - 它在测试中。您需要测试长度,Flavor.batch_eligible.length
产生我想要的整数 1。
也许其他人都知道 - 我不知道。
谢谢塔林
我之前试着问过这个问题,但并不顺利 - 希望这次我做得更好。
我有三个模型
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.count
或 puts Flavor.batch_eligible.size
都产生相同的结果,例如,散列 {312 => 1} - 312 将是 Factory Created Flavor 的 ID。
所以问题(一旦我解决了 flavors.id)不在范围内 - 它在测试中。您需要测试长度,Flavor.batch_eligible.length
产生我想要的整数 1。
也许其他人都知道 - 我不知道。
谢谢塔林