多态关联的 ActiveRecord 模型“类型”列包括关联模型的命名空间

pro*_*fan 2 activerecord ruby-on-rails

问题

如何告诉 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
Run Code Online (Sandbox Code Playgroud)

总结一下:

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

我想做的改变:

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

module PagesEngine
  class PageTemplateA < ActiveRecord::Base
    has_many :type_a_pages, :as => :pageable, :class_name => 'TypeAPage', :inverse_of => :pageable
  end
end
Run Code Online (Sandbox Code Playgroud)

这工作得很好,除了ActiveRecord 推断pageable_typeforTypeAPagePagesEngine::PageTemplateA

我如何告诉 ActiveRecord 不包含名称空间,并解析pageable_typePageTemplateA而不是PagesEngine::PageTemplateA

Ric*_*lse 5

对于保存多态关系的模型,您可以设置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
Run Code Online (Sandbox Code Playgroud)

使用与关联模型 (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
Run Code Online (Sandbox Code Playgroud)

最后重写belongs_to方法并自定义Active Record属于多态关联。

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
Run Code Online (Sandbox Code Playgroud)