私はここで多態性関連とSTIの事例を持っています。
# app/models/car.rb
class Car < ActiveRecord::Base
belongs_to :borrowable, :polymorphic => true
end
# app/models/staff.rb
class Staff < ActiveRecord::Base
has_one :car, :as => :borrowable, :dependent => :destroy
end
# app/models/guard.rb
class Guard < Staff
end
ポリモーフィックな関連付けが機能するためには、ポリモーフィックな関連付けに関するAPIドキュメントに従って http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations 設定する必要があることborrowable_type
からbase_class
of STIモデル、つまり私の場合はStaff
です。
質問は、次のとおりです。なぜborrowable_type
STIクラスに設定しますか?
それを証明するいくつかのテスト:
# now the test speaks only truth
# test/fixtures/cars.yml
one:
name: Enzo
borrowable: staff (Staff)
two:
name: Mustang
borrowable: guard (Guard)
# test/fixtures/staffs.yml
staff:
name: Jullia Gillard
guard:
name: Joni Bravo
type: Guard
# test/units/car_test.rb
require 'test_helper'
class CarTest < ActiveSupport::TestCase
setup do
@staff = staffs(:staff)
@guard = staffs(:guard)
end
test "should be destroyed if an associated staff is destroyed" do
assert_difference('Car.count', -1) do
@staff.destroy
end
end
test "should be destroyed if an associated guard is destroyed" do
assert_difference('Car.count', -1) do
@guard.destroy
end
end
end
しかし、Staffインスタンスでのみ当てはまるようです。結果は次のとおりです。
# Running tests:
F.
Finished tests in 0.146657s, 13.6373 tests/s, 13.6373 assertions/s.
1) Failure:
test_should_be_destroyed_if_an_associated_guard_is_destroyed(CarTest) [/private/tmp/guineapig/test/unit/car_test.rb:16]:
"Car.count" didn't change by -1.
<1> expected but was
<2>.
ありがとう
良い質問。私はRails 3.1を使用してまったく同じ問題を抱えていました。これは機能しないため、これを行うことができないようです。おそらく意図された動作です。明らかに、単一のテーブルと組み合わせて多態的な関連付けを使用していますRailsの継承(STI)は少し複雑です。
現在のRailsのドキュメントRails 3.2は、 polymorphic associationsとSTI)を組み合わせるためのこのアドバイスを提供します :
単一テーブル継承(STI)と組み合わせてポリモーフィックな関連付けを使用するのは少し注意が必要です。関連付けが期待どおりに機能するようにするには、STIモデルの基本モデルを多態性の関連付けのタイプ列に格納してください。
あなたの場合、基本モデルは「スタッフ」になります。つまり、「ガード」ではなく、「borrowable_type」はすべてのアイテムで「スタッフ」になります。 「becomes」を使用して、派生クラスを基本クラスとして表示させることができます:guard.becomes(Staff)
。列「borrowable_type」を基本クラス「Staff」に直接設定するか、Railsドキュメントが示唆するように、
class Car < ActiveRecord::Base
..
def borrowable_type=(sType)
super(sType.to_s.classify.constantize.base_class.to_s)
end
古い質問ですが、Rails 4の問題はまだ残っています。別のオプションは、_type
メソッドを動的に作成/上書きすることです。これは、アプリが複数のSTIとのポリモーフィックな関連付けで、ロジックを1か所に保持したい。
この問題により、すべての多態的な関連付けが取得され、レコードが常に基本クラスを使用して保存されるようになります。
# models/concerns/single_table_polymorphic.rb
module SingleTablePolymorphic
extend ActiveSupport::Concern
included do
self.reflect_on_all_associations.select{|a| a.options[:polymorphic]}.map(&:name).each do |name|
define_method "#{name.to_s}_type=" do |class_name|
super(class_name.constantize.base_class.name)
end
end
end
end
次に、それをモデルに含めます。
class Car < ActiveRecord::Base
belongs_to :borrowable, :polymorphic => true
include SingleTablePolymorphic
end
Rails 4.2
でこの問題が発生しました。私は解決する2つの方法を見つけました:
-
問題は、RailsがSTI関係のbase_class
名を使用することです。
この理由は他の回答に記載されていますが、要点は、コアチームがtableを参照できるはずだと感じているように見えることですポリモーフィックSTIアソシエーションのclassではなく。
私はこの考えに同意しませんが、Railsコアチームの一部ではないので、それを解決するための多くの情報を持っていません。
修正するには2つの方法があります。
-
class Association < ActiveRecord::Base
belongs_to :associatiable, polymorphic: true
belongs_to :associated, polymorphic: true
before_validation :set_type
def set_type
self.associated_type = associated.class.name
end
end
これにより、データベースにデータを作成する前に{x}_type
レコードが変更されます。これは非常にうまく機能し、関連付けの多態性を保持しています。
ActiveRecord
メソッドをオーバーライドする#app/config/initializers/sti_base.rb
require "active_record"
require "active_record_extension"
ActiveRecord::Base.store_base_sti_class = false
#lib/active_record_extension.rb
module ActiveRecordExtension #-> http://stackoverflow.com/questions/2328984/Rails-extending-activerecordbase
extend ActiveSupport::Concern
included do
class_attribute :store_base_sti_class
self.store_base_sti_class = true
end
end
# include the extension
ActiveRecord::Base.send(:include, ActiveRecordExtension)
####
module AddPolymorphic
extend ActiveSupport::Concern
included do #-> http://stackoverflow.com/questions/28214874/overriding-methods-in-an-activesupportconcern-module-which-are-defined-by-a-cl
define_method :replace_keys do |record=nil|
super(record)
owner[reflection.foreign_type] = ActiveRecord::Base.store_base_sti_class ? record.class.base_class.name : record.class.name
end
end
end
ActiveRecord::Associations::BelongsToPolymorphicAssociation.send(:include, AddPolymorphic)
問題を修正するより体系的な方法は、それを管理するActiveRecord
コアメソッドを編集することです。 this gem で参照を使用して、修正またはオーバーライドする必要のある要素を見つけました。
これはテストされておらず、ActiveRecordコアメソッドの他の部分の拡張機能が必要ですが、私のローカルシステムでは機能するようです。
宝石があります。 https://github.com/appfolio/store_base_sti_class
テスト済みで、ARのさまざまなバージョンで動作します。
ポリモーフィック型のhas_*
関連付けのカスタムスコープを構築することもできます。
class Staff < ActiveRecord::Base
has_one :car,
->(s) { where(cars: { borrowable_type: s.class }, # defaults to base_class
foreign_key: :borrowable_id,
:dependent => :destroy
end
ポリモーフィック結合は複合外部キー(* _idおよび* _type)を使用するため、type句に正しい値を指定する必要があります。ただし、_idは、ポリモーフィック関連付けの名前を指定するforeign_key
宣言だけで機能するはずです。
ポリモーフィズムの性質上、Railsアプリケーション内の任意のモデルである可能性があるため、whatモデルは借用可能であると知ってイライラすることがあります。この関係は、借用可能オブジェクトのカスケード削除を適用するすべてのモデルで宣言する必要があります。
これはもっと簡単なはずであるという一般的なコメントに同意します。とはいえ、ここで私のために働いたものです。
次のように、基本クラスとして会社、STIクラスとして顧客とプロスペクトを使用したモデルがあります。
class Firm
end
class Customer < Firm
end
class Prospect < Firm
end
また、次のような多態性クラスOpportunityもあります。
class Opportunity
belongs_to :opportunistic, polymorphic: true
end
機会をどちらかと言いたい
customer.opportunities
または
prospect.opportunities
そのために、モデルを次のように変更しました。
class Firm
has_many opportunities, as: :opportunistic
end
class Opportunity
belongs_to :customer, class_name: 'Firm', foreign_key: :opportunistic_id
belongs_to :prospect, class_name: 'Firm', foreign_key: :opportunistic_id
end
Opportunistic_typeを 'Firm'(基本クラス)にして、それぞれの顧客または見込み客IDをopportunistic_idとして商談を保存します。
これで、customer.opportunitiesとpromise.opportunitiesを希望どおりに取得できます。