我们正在构建一种地址簿,我们有用户,联系人和名为UserEntity的父表。我们希望通过名为Relationship的表将它们与多对多关联联系起来,这种关系必须与UserEntity表中的自相关。
一个用户有很多联系人,一个联系人有很多用户。
稍后我们需要将新模型与用户联系起来,因此关系模型必须是多态的。我们已经建立了这个:
class UserEntity < ActiveRecord::Base
self.table_name = "users"
end
class User < UserEntity
has_many :relationships, as: :relatable
has_many :contacts, through: :relationships, source: :relatable, source_type "Contact"
end
class Contact < UserEntity
has_many :relationships, as: :relatable
has_many :users, through: :relationships, source: :relatable, source_type "User"
end
class Relationship < ActiveRecord::Base
belongs_to :user
belongs_to :relatable, polymorphic: true
end
但是当我们尝试保存关联user.contacts << contact
时,relatable_type
字段会保存&#34; UserEntity&#34;价值而不是&#34;联系&#34;或&#34;用户&#34;值。我们已经尝试过这样做而没有任何有利的结果http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations
我们如何使relatable_type
值保存模型名称(&#34;联系&#34; ||&#34;用户&#34;)而不是STI父模型名称(&#34; UserEntity&#34;?)
答案 0 :(得分:1)
这称为STI
base class
,即使您已调用Parent
,Rails也会保存subclass
类。我们有经验:
以上内容应Node
为Company
...
gem
名为store_sti_base_class
:
请注意
addressable_type
列为Person
,即使实际的班级为Vendor
。通常,这不是问题,但在某些情况下它可能具有负面的性能特征。最明显的一个是需要与人员联系或额外查询才能找出实际的可寻址类型。
围绕它的方式 - 至少从gem
的角度来看 - 是扩展ActiveRecord
以便它将保存模型的实际类,而不是它的父类。
我唯一的代码 - 过时了 - 如下:
#config/initializers/sti_base.rb
ActiveRecord::Base.store_base_sti_class = false
#lib/extensions/sti_base_class.rb
module Extensions
module STIBaseClass #-> 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, STIBaseClass)
####
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
Rails.logger.info record.class.base_class.name
end
end
end
ActiveRecord::Associations::BelongsToPolymorphicAssociation.send(:include, AddPolymorphic)
end
因为我需要让这个工作,所以可能值得玩弄一些东西。
答案 1 :(得分:0)
您必须将UserEntity设置为抽象类:
class UserEntity < ActiveRecord::Base
self.table_name = "users"
self.abstract_class = true
end
然后,值relatable_type
可以是User
或Contact
。
根据您的情况,默认RoR保存为relatable_type
方法的base_class
值:
class UserEntity < ActiveRecord::Base
end
class User < UserEntity
end
class Contact < UserEntity
end
irb(main):010:0> User.base_class.name
=> "UserEntity"
irb(main):011:0> Contact.base_class.name
=> "UserEntity"
将UserEntity设为抽象类时:
class UserEntity < ActiveRecord::Base
self.abstract_class = true
end
class User < UserEntity
end
class Contact < UserEntity
end
irb(main):010:0> User.base_class.name
=> "User"
irb(main):011:0> Contact.base_class.name
=> "Contact"
此处定义了存储类名称(https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb#L14)的方法:
def replace_keys(record)
super
owner[reflection.foreign_type] = record ? record.class.polymorphic_name : nil
end
def polymorphic_name
base_class.name
end