我在Rails 5.1应用程序中有两个模型。 Master是主要模型,Item属于它。对于特殊用例,我需要覆盖master.items.build
的#build方法。在这个新的构建方法中,我必须进行查询以检查其他项目中的某些条件。
执行此操作时,我会遇到一些意外(由我)行为,这样关联块似乎会改变查询的范围。
master.rb
class Master < ApplicationRecord
has_many :items, inverse_of: :master do
def build att=nil
item = self.new(master: proxy_association.owner)
item.description1 = self.where(master_id: nil).to_sql
item.description2 = self.unscoped.where(master_id: nil).to_sql
item
end
end
end
item.rb的
class Item < ApplicationRecord
belongs_to :master
before_create -> {
self.description1 = self.class.where(master_id: nil).to_sql
self.description2 = self.class.unscoped.where(master_id: nil).to_sql
}
end
master_test.rb
require 'test_helper'
class MasterTest < ActiveSupport::TestCase
test "master build outside association works as expected" do
m = Master.create! name: 'Bob'
item = Item.create master: m, name: 'Wall'
puts "Master: #{m}"
puts "self.where(master_id: nil).to_sql"
puts item.description1
puts "self.unscoped.where(master_id: nil).to_sql"
puts item.description2
# Flawed understanding would expect the queries to be the same
assert item.description1 == item.description2
end
test "master association blocks add an unexpected scope" do
m = Master.create! name: 'Bob'
item = m.items.build name: 'Wall'
puts "Master: #{m}"
puts "self.where(master_id: nil).to_sql"
puts item.description1
puts "self.unscoped.where(master_id: nil).to_sql"
puts item.description2
# Flawed understanding would expect the queries to be the same
assert item.description1 == item.description2
end
end
简而言之,第一次测试的结果符合预期。无范围和标准查询都按预期运行。第二个测试的结果,在关联块内调用相同的查询导致:
Master: #<Master:0x0055c3ee11b770>
self.where(master_id: nil).to_sql
SELECT "items".* FROM "items" WHERE "items"."master_id" = 980190963 AND "items"."master_id" IS NULL
self.unscoped.where(master_id: nil).to_sql
SELECT "items".* FROM "items" WHERE "items"."master_id" IS NULL
我没有在任何地方看到这个记录,并希望有人能指出我在关联块内部进行自动范围界定的原因,以及解释的地方。我会毫无疑问地接受它,但这种行为似乎在某些时候发生了变化(基于已经开始失败的测试的应用程序)。我也在旧版本的4.2.9应用程序中体验过它,我知道它在过去没有这个范围的情况下工作。
如果需要重现,这是一个包含测试应用的GitHub仓库:https://github.com/philayres/test_assoc_blocks
应该像rails db:migrate
和rake test
一样简单来演示它。
我愿意接受想法和解释。
答案 0 :(得分:0)
也许我没有正确理解这一点:
m = Master.create! name: 'Bob'
item = m.items.build name: 'Wall'
puts "Master: #{m}"
puts "self.where(master_id: nil).to_sql"
puts item.description1
puts "self.unscoped.where(master_id: nil).to_sql"
puts item.description2
# Flawed understanding would expect the queries to be the same
assert item.description1 == item.description2
在这种情况下,您将范围传递给构建方法:
item = m.items.build name: 'Wall'
此范围是Item.where(master_id:m.id)。
在覆盖的构建方法中,您可以执行以下操作:
item.description1 = self.where(master_id: nil).to_sql
self将成为此类的范围实例 - 将成为
Item.where(master_id: m.id).where(master_id: nil).to_sql
对于第二种方法
self.unscoped.where(master_id: nil).to_sql
将删除第一个范围,然后应用where子句。我不确定你为什么要打电话
m.items
如果您特别不希望与m。
相关联的项目要验证这一点,请尝试检查
self.to_sql
并且自己实际上在街区内。