简体   繁体   English

Ruby On Rails-使用HAVING覆盖作用域的JOIN

[英]Ruby On Rails - Coverting a JOIN using HAVING to a scope

I tried to ask this question previously and it didn't go well - hopefully I do it better this time. 我以前曾尝试问过这个问题,但进展不顺利-希望这次我做得更好。

I have three models 我有三种模式

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

Batches are made of flavors, but a flavor can only be made into a batch if it's components add up to 100 percent (hence why I put the percentage validation in there so it was represented). 批次是由调味料制成的,但是只有当调味料的成分加起来达到100%时,才可以将其制成批次(因此,为什么要在其中放置百分比验证以便如此表示)。

At first I tried to write this as a scope, but could never get it to work, the model testing i created worked using 最初,我尝试将其编写为作用域,但无法使其正常工作,我创建的模型测试可以使用

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

I did make an attempt at the scope and it failed. 我确实尝试过示波器,但失败了。 Here is the final version of the scope I came up with: 这是我想出的范围的最终版本:

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

The resultant object will be used to populate a selection list in a form for batches (flavors can exist before the components are fully worked out). 生成的对象将用于以批次的形式填充选择列表(味道可能在组件完全加工之前就存在)。

I figure the limitation here is my understanding of scopes - so how would the scope be built properly to produce the same results as the find_by_sql expression? 我认为这里的限制是我对范围的理解-那么如何正确构建范围以产生与find_by_sql表达式相同的结果?

All help is appreciated, thanks. 感谢所有帮助,谢谢。

In response to the first comment - I tried a variety of scopes without capturing the errors - the scope above returns this error: 作为对第一条评论的回应-我尝试了多种范围而不捕获错误-上面的范围返回了此错误:

 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

changing it to flavors id makes it 'work' but it doesn't return the proper information. 将其更改为flavor id使其“可用”,但未返回正确的信息。

One more piece of code - models testing 另一段代码-模型测试

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

Even with a clean test database - the batch_eligible count is 4 - not one 即使使用干净的测试数据库-batch_合格的计数为4-不是一个

One more note - the tests DO pass with the find_by_sql function being used - I just think a scope should be possible? 还有一点需要注意-测试必须通过使用find_by_sql函数才能通过-我只是认为范围应该可行?

Props to @taryneast for the help - I was pointed in the right direction by this. 向@taryneast寻求帮助的建议-这样就为我指明了正确的方向。

After correcting the scope issue with flavors.id - I did run the inspect to see what was happening but I also ran a variety of functions. 在用flavors.id纠正了范围问题之后,我确实进行了检查以查看发生了什么,但我还运行了各种功能。

puts Flavor.batch_eligible.count or puts Flavor.batch_eligible.size both yield the same thing, for example, the hash {312 => 1} - 312 would be the id of the Factory Created Flavor. puts Flavor.batch_eligible.countputs Flavor.batch_eligible.size都产生相同的结果,例如,哈希{312 => 1} puts Flavor.batch_eligible.size是工厂创建的Flavor的ID。

So the problem (once I solved flavors.id) wasn't in the scope - it was in the test. 因此,问题(一旦我解决了flavors.id)就不在范围内了,而是在测试中。 You need to test the LENGTH, Flavor.batch_eligible.length yields the integer 1 that I wanted. 您需要测试LENGTH, Flavor.batch_eligible.length产生我想要的整数1。

Perhaps everyone else knew that - I didn't. 也许其他所有人都知道-我不知道。

Thank you Taryn 谢谢塔琳

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM