ここではRails 2.3.14を使用していますが、この特定のバージョンに限定されない問題が発生する可能性があります。これはすべて、Rails 3 にまだ存在するアソシエーションと熱心な読み込み機能であり、2.3.14 よりもかなり前から存在していました。私はレール 2.3.8 からのアップグレードの過程にあり、以下に説明する問題はありませんでした。
以下のコードは、はるかに複雑な生産システムに基づくモックアップです。私が概説しているクラス/モジュールスキームは、理由によりこのように設定されています。このモックアップには、問題を説明するために必要以上の詳細が含まれています。うまくいけば、システムの全体的な構造がより明確になります。
車両 (車/トラック) やゴルフ ボールなど、「運転」できるドメイン オブジェクトがいくつかあるとします。これらのそれぞれについて、ActiveRecord クラスがあります。
class Vehicle < ActiveRecord::Base
end
class Car < Vehicle
include Driveable
end
class Truck < Vehicle
include Driveable
end
class GolfBall < ActiveRecord::Base
include Driveable
end
最初に、GolfBall
はトップレベルのモデル クラスであり、対応するデータベース テーブルはgolf_balls
です。一方、Car
とTruck
は のサブクラスですVehicle
。車両用のデータベースはSTIで設定されているのでCars
、 と両方ともテーブルTrucks
に対応しています(列微分器付き)。vehicles
type
Drivable
次に、すべての最下位レベルのドメイン オブジェクト ( Car
、Truck
、 ) にモジュールを含めていることに注意してください。GolfBall
これは次のようになります (実際のシステムでは、このモジュールは、特定のものに基づいて物事を設定するなど、さらに多くのことを行います。ドメイン オブジェクト):
module Driven
def self.included(base)
base.class_eval do
has_one :driver, :as => :driveable, :class_name => "#{self.name}Driver", :dependent => :destroy
end
end
end
したがって、これらのそれぞれにを含めることができ、インクルード クラス名に基づいてDriver
を使用しています (たとえば、クラスをインクルードすると with になります)。これらの参照されるクラス (など) にはそれぞれに必要な特定のビジネス ロジックが含まれているためです。協会の使用。:class_name
Car
has_one
:class_name => "CarDriver"
CarDriver
Driver
ポリモーフィック アソシエーションをセットアップする最上位クラスと、ドメイン オブジェクト ドライバー用の上記と同様のサブクラス階層があります。
class Driver < ActiveRecord::Base
belongs_to :driveable, :polymorphic => true
end
class VehicleDriver < Driver
end
class CarDriver < VehicleDriver
end
class TruckDriver < VehicleDriver
end
class GolfBallDriver < Driver
end
これは、単一のデータベース テーブルに基づいており、drivers
すべてのサブクラスに STI を使用しています。
このシステムを配置したら、新しいCar
(以下に保存されて@car
いる) を作成し、このように新しく作成されたものと関連付けCarDriver
ます (このモックアップでは、実際のシステムの動作を反映するために、これらの特定の順次ステップに分割されています)。
@car = Car.create
CarDriver.create(:driveable => @car)
vehicles
これにより、次のようにテーブルにデータベース行が作成されました。
id type ...
-----------------
1 Car ...
そして、drivers
このようなテーブルの行:
id driveable_id driveable_type type ...
--------------------------------------------------------
1 1 Vehicle CarDriver ...
Vehicle
車両がSTIであるためとはdriveable_type
対照的です。Car
ここまでは順調ですね。ここで、Rails コンソールを開き、簡単なコマンドを実行してCar
インスタンスを取得します。
>> @car = Car.find(:last)
=> #<Car id: 1, type: "Car", ...>
ログによると、実行されたクエリは次のとおりです。
Car Load (1.0ms)
SELECT * FROM `vehicles`
WHERE ( `vehicles`.`type` = 'Car' )
ORDER BY vehicles.id DESC
LIMIT 1
次に、次を取得しCarDriver
ます。
>> @car.driver
=> #<CarDriver id: 1, driveable_id: 1, driveable_type: "Vehicle", type: "CarDriver", ...>
これにより、このクエリが実行されました。
CarDriver Load (0.7ms)
SELECT * FROM `drivers`
WHERE (`drivers`.driveable_id = 1 AND `drivers`.driveable_type = 'Vehicle') AND (`drivers`.`type` = 'CarDriver' )
LIMIT 1
ただし、熱心な読み込みを使用しようとすると、異なる結果が得られます。新しいコンソール セッションから、次を実行します。
>> @car = Car.find(:last, :include => :driveable)
=> #<Car id: 1, type: "Car", ...>
>> @car.driver
=> nil
これnil
は、ドライバーにとっての結果となります。ログを確認すると、最初のステートメントで次のクエリが実行されます (通常のクエリと熱心な読み込みクエリ)。
Car Load (1.0ms)
SELECT * FROM `vehicles`
WHERE ( `vehicles`.`type` = 'Car' )
ORDER BY vehicles.id DESC
LIMIT 1
CarDriver Load (0.8ms)
SELECT * FROM `drivers`
WHERE (`drivers`.driveable_id = 1 AND `drivers`.driveable_type = 'Car') AND (`drivers`.`type` = 'CarDriver' )
LIMIT 1
ご覧のとおり、熱心な読み込みの場合、Car
クエリは上記と同じですが、CarDriver
クエリは異なります。ほとんど同じですが、非イーガー ローディングの場合と同様に、drivers.driveable
タイプの場合はCar
STI 基底クラス名を探すべき場所を探します。Vehicle
これを修正する方法はありますか?