Why polymorphic association doesn't work for STI if type column of the polymorphic association doesn't point to the base model of STI?

前端 未结 7 2034
旧时难觅i
旧时难觅i 2020-12-02 12:07

I have a case of polymorphic association and STI here.

# app/models/car.rb
class Car < ActiveRecord::Base
  belongs_to :borrowable, :polymorphic => tru         


        
7条回答
  •  挽巷
    挽巷 (楼主)
    2020-12-02 12:42

    Just had this issue in Rails 4.2. I found two ways to resolve:

    --

    The problem is that Rails uses the base_class name of the STI relationship.

    The reason for this has been documented in the other answers, but the gist is that the core team seem to feel that you should be able to reference the table rather than the class for a polymorphic STI association.

    I disagree with this idea, but am not part of the Rails Core team, so don't have much input into resolving it.

    There are two ways to fix it:

    --

    1) Insert at model-level:

    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
    

    This will change the {x}_type record before the creation of the data into the db. This works very well, and still retains the polymorphic nature of the association.

    2) Override Core ActiveRecord methods

    #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)
    

    A more systemic way to fix the issue is to edit the ActiveRecord core methods which govern it. I used references in this gem to find out which elements needed to be fixed / overridden.

    This is untested and still needs extensions for some of the other parts of the ActiveRecord core methods, but seems to work for my local system.

提交回复
热议问题