Rails 5.1アプリには2つのモデルがあります。マスターはプライマリモデルであり、Itemはそれに属します。特別な使用例については、私はmaster.items.build
の#buildメソッドをオーバーライドする必要があります。この新しいビルドメソッドでは、他の項目の特定の条件を調べるためのクエリを作成する必要があります。Rails関連ブロックはクエリスコープに影響します。どうして?
これを実行すると、関連ブロックがクエリの範囲を変更するように見えるような予期しない(私自身の)動作が発生します。
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
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
、再
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
item.rb最初のテストのsultは期待どおりに動作します。私は、これはどこかに文書化し、誰かが私を指すことができることを期待しています見ていない
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
:どちらも、スコープ外と標準クエリはに関連ブロック結果内の同じクエリを呼び出して、第二の試験のexpected.The結果として機能しますアソシエーションブロック内のこの自動スコープの理由、およびそれが説明されている場所。私はそれを疑いなく受け入れるだろうが、この振る舞いは(テストが失敗し始めたアプリケーションに基づいて)ある時点で変更されたように見える。私は古いバージョン4.2.9のアプリでもこれを体験しました。ここでは、過去にこのスコープなしで動作することが分かっています。再現するために必要であれば、ここで
は、テストアプリでGitHubのレポです:https://github.com/philayres/test_assoc_blocks
は、それを証明するためにrails db:migrate
とrake test
のように単純であるべき。
私はアイデアと説明について開いています。
ありがとうございます。問題は、私が明示的に定義していない範囲が適用されているのを見ていることです。魔法の範囲は、私が構築している親の「マスター」オブジェクトに基づいているように見えます。問合せは暗黙的に次のようにスコープされます。 'SELECT "items"。* FROM "items" WHERE "items"。 "master_id" = 980190963 [...] '。これまでのあなたの答えに基づいて、私はこのスコープが突然現れる理由をまだ見ていません。私はなぜこのスコープが他の場所にも見えないときにアソシエーションブロックの内側に適用される理由についての説明を探しています。 – Phil
申し訳ありませんが、あなたはm.itemsを呼び出すときにそれを見ていませんか?それはこのスコープを適用するものです。 – oowowaee
はい、私はそれがスコープを適用している理由を理解していません。ブロックの外にm.itemsを使用すると、スコープはありません。アソシエーションブロックでスコープが適用される理由を説明する説明がありますか? – Phil