STI模型有许多通过,自相关和多态的方法来保存STI父类名而不是STI子类名

时间:2016-02-11 02:08:01

标签: ruby-on-rails

我们正在构建一种地址簿,我们有用户,联系人和名为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;?)

2 个答案:

答案 0 :(得分:1)

这称为STI base class,即使您已调用Parent,Rails也会保存subclass类。我们有经验:

enter image description here

以上内容应NodeCompany ...

去年我花了一些时间来看这个;有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可以是UserContact

根据您的情况,默认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