save していない ActiveRecord インスタンスで、親知らずな子レコードが取得される

save していない ActiveRecord インスタンスで、association メソッドと、association 先の scope を chain した際の挙動に違和感を感じたので書いてみます

# Rails 3.2.3 で試しています

Model を用意して、

class Firm < ActiveRecord::Base
  has_many :clients
end
class Client < ActiveRecord::Base
  belongs_to :firm
end

データを作成します

firm = Firm.create! name: '37signals'
# => #<Firm id: 1, name: "37signals">
firm.clients << Client.new(name: 'WWF')
# => [#<Client id: 1, name: "WWF", firm_id: 1>]
firm.clients << Client.new(name: 'patagonia')
# => [#<Client id: 1, name: "WWF", firm_id: 1>, #<Client id: 2, name: "patagonia", firm_id: 1>]


この状態で、Firm#clients を実行した結果は以下です

firm.clients
# => [#<Client id: 1, name: "WWF", firm_id: 1>, #<Client id: 2, name: "patagonia", firm_id: 1>]

つぎは、order という scope を chain して実行した結果です

firm.clients.order :name
# => [#<Client id: 2, name: "patagonia", firm_id: 1>, #<Client id: 1, name: "WWF", firm_id: 1>]

"association メソッドと、association 先の scope を chain した" というのはこのケースをいっています
ここまでは、まったくイメージ通りで、問題なしです


つぎに、少しデータを編集します

firm.clients = []
# => []

上を実行すると内部的には以下の SQL が実行され

UPDATE `clients` SET `firm_id` = NULL WHERE `clients`.`firm_id` = 1 AND `clients`.`id` IN (1, 2)

"37signals" の clients として、"WWF", "patagonia" は参照できない状態になります

この状態で以下を実行した場合の結果は何でしょうか?

Firm.new.clients

空の配列です

# => []

では、以下を実行した結果は何でしょうか?

Firm.new.clients.order :name

んんん、以下になりますー

# => [#<Client id: 2, name: "patagonia", firm_id: nil>, #<Client id: 1, name: "WWF", firm_id: nil>]

改めて実行してみると、違和感どころの騒ぎじゃないような


実行される SQL は、以下となっていて気持ちは伝わってきますが、こちらの気持ちは汲み取ってもらえていないですね

SELECT `clients`.* FROM `clients` WHERE `clients`.`firm_id` IS NULL ORDER BY name


ちなみに、
Rails 2.3.14 で、同じことをすると イメージ通りの結果がかえります
# order という scope は、Client に自前で実装している前提です

Firm.new.clients.order :name
# => []

実行される SQL は以下となっています

SELECT * FROM `clients` WHERE (`clients`.firm_id = NULL) ORDER BY name

どっちにしても、
データベースに保存されていないインスタンスが、association メソッドを実行する場合、データベースへの問い合わせは必要ないんじゃないだろうか