【问题标题】:ActiveRecord model 'type' column of polymorphic association includes namespace of associated model多态关联的 ActiveRecord 模型“类型”列包括关联模型的命名空间
【发布时间】:2016-10-04 16:19:52
【问题描述】:

问题

如何告诉 ActiveRecord 在存储/查询关联类型列时不包含关联类的命名空间?

现状:

考虑以下类定义:

class PageTemplateA < ActiveRecord::Base
  has_many :type_a_pages, :as => :pageable, :class_name => 'TypeAPage', :inverse_of => :pageable
end

##### The following class is implemented through STI.
class TypeAPage < Page
  belongs_to :pageable, :class_name => 'PageTemplateA', :inverse_of => :type_a_page
end

class Page < ActiveRecord::Base
  belongs_to :pageable, :polymorphic => true
end

总结一下:

  • TypeAPage是通过数据库表中的STI实现的,pages
  • TypeAPage 通过多态关联与PageTemplateA 关联(pages.pageable_type 在与PageTemplateA 关联时为PageTemplateA

我想做的改变:

我想将上述所有模型移动到一个新的命名空间中,比如PagesEngine,所以我对PageTemplateA 的定义如下:

module PagesEngine
  class PageTemplateA < ActiveRecord::Base
    has_many :type_a_pages, :as => :pageable, :class_name => 'TypeAPage', :inverse_of => :pageable
  end
end

这很好用,除了 ActiveRecord 将TypeAPagepageable_type 推断为PagesEngine::PageTemplateA

如何告诉 ActiveRecord 不包含命名空间,并将 pageable_type 解析为 PageTemplateA 而不是 PagesEngine::PageTemplateA

【问题讨论】:

    标签: ruby-on-rails activerecord


    【解决方案1】:

    对于持有多态关系的模型,你可以设置self.store_full_sti_class = false或者继承一个抽象模型,将store_full_sti_class设置为false。

    module PagesEngine
      class Page < Base
        # self.store_full_sti_class = false
        belongs_to :pageable, polymorphic: true
      end
    
      class TypeAPage < Page
        belongs_to :pageable, 
                   class_name: 'PageTemplateA', 
                   inverse_of: :type_a_pages
      end
    end
    

    使用与关联模型 (PagesEngine::PageTemplateA) 匹配但没有命名空间的类型列 (pageable_type) 显式限定关系 (type_as_pages)。

    module PagesEngine
      class PageTemplateA < Base
        has_many :type_a_pages, 
                 -> { where(pageable_type: 'PageTemplateA') }, 
                 foreign_key: :pageable_id, 
                 inverse_of: :pageable
      end
    end
    

    最后重写belongs_to方法,自定义Active Record Belongs To Polymorphic Association。

    module PagesEngine
      class Base < ActiveRecord::Base
        self.abstract_class = true
        self.store_full_sti_class = false
    
        def self.belongs_to(name, scope = nil, options = {})
          super(name, scope, options).tap do |hash|
            reflection = hash[name.to_s]
            def reflection.association_class
              if polymorphic?
                BelongsToPolymorphicAssociation
              else
                super
              end
            end unless store_full_sti_class
          end
        end
      end
    
      class BelongsToPolymorphicAssociation < ActiveRecord::Associations::BelongsToPolymorphicAssociation
        def klass
          type = owner[reflection.foreign_type]
          type.presence && PagesEngine.const_get(type)
        end
      end
    end
    

    【讨论】:

      【解决方案2】:

      使用self.sti_name 覆盖相应类的类型列名称:

      module PagesEngine
        class PageTemplateA < ActiveRecord::Base
          def self.sti_name
            "PageTemplateA"
          end
        end
      end
      

      您可能会发现以下博客文章很有用:https://lorefnon.me/2014/07/27/optimizing-sti-columns.html

      【讨论】:

      • 感谢您的回复,安东尼。但是,在这种情况下,PageTemplateA 上没有实施 STI。它实际上是在TypeAPage
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-28
      • 2012-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-30
      • 1970-01-01
      相关资源
      最近更新 更多