【发布时间】: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, -> {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