【问题标题】:Finding all by Polymorphic Type in Rails?在 Rails 中按多态类型查找所有内容?
【发布时间】:2011-01-19 21:22:07
【问题描述】:

有没有办法在 Rails 中找到特定多态类型的所有多态模型?因此,如果我的 Group、Event 和 Project 都带有如下声明:

has_many :assignments, :as => :assignable

我可以这样做吗:

Assignable.all

...或

BuiltInRailsPolymorphicHelper.all("assignable")

那就太好了。

编辑:

... 这样Assignable.all 返回[Event, Group, Product](类数组)

【问题讨论】:

  • 我已根据您提供的其他信息更改了答案。我认为它会满足您的需求。

标签: ruby-on-rails polymorphic-associations helpers


【解决方案1】:

没有直接的方法。我为ActiveRecord::Base 编写了这个猴子补丁。 这适用于任何课程。

class ActiveRecord::Base

  def self.all_polymorphic_types(name)
    @poly_hash ||= {}.tap do |hash|
      Dir.glob(File.join(Rails.root, "app", "models", "**", "*.rb")).each do |file|
        klass = File.basename(file, ".rb").camelize.constantize rescue nil
        next unless klass.ancestors.include?(ActiveRecord::Base)

        klass.
          reflect_on_all_associations(:has_many).
          select{ |r| r.options[:as] }.
          each do |reflection|
            (hash[reflection.options[:as]] ||= []) << klass
          end
      end
    end
    @poly_hash[name.to_sym]
  end

end

现在您可以执行以下操作:

Assignable.all_polymorphic_types(:assignable).map(&:to_s)
# returns ['Project', 'Event', 'Group']

【讨论】:

  • 如何实现这个?我应该去 ActiveRecord::Base 类进行修改吗?在我的项目中似乎找不到这个类..
  • @james 参考这个答案:stackoverflow.com/questions/2328984/…
  • @AugustinRiedinger 在 prod 模式下,您可以使用 ActiveRecord::Base.subclasses,在 dev 模式下,只有在明确引用类后才会加载这些类。所以不太靠谱。我的方法读取文件名(而不是内容),并且在整个过程的生命周期中完成一次。所以可以忍受..
  • @AugustinRiedinger 调用 ActiveRecord::Base. all_polymorphic_types 加载所有类。
  • 太棒了。这确实应该是 AR 本身的一部分。例如对于构建表单非常有帮助。
【解决方案2】:

您也可以尝试这种方式。因为上面的解决方案对我不起作用,因为我有一些 mongo 的模型。

def get_has_many_associations_for_model(associations_name, polymorphic=nil)

  associations_name = associations_name.to_s.parameterize.underscore.pluralize.to_sym
  active_models = ActiveRecord::Base.descendants
  get_model = []
  active_models.each do |model|

    has_many_associations =model.reflect_on_all_associations(:has_many).select{|a|a.name==associations_name }
    has_many_associations = has_many_associations.select{ |a| a.options[:as] == polymorphic.to_s.to_sym} if polymorphic.present?
    get_model << model if has_many_associations.present?

  end
  get_model.map{|a| a.to_s}
end

Anb 这么称呼它

get_has_many_associations_for_model("assignments", "assignable")

这里第二个参数是可选的,如果你想要多态记录而不是传递它,否则将其留空。

它将模型名称数组作为字符串返回。

【讨论】:

    【解决方案3】:

    Harish Shetty's solution 不适用于未直接存储在 Rails.root/app/models 中而是在子目录中的命名空间模型文件。尽管它正确地将文件放在子目录中,但在将文件名转换为常量时,它无法包含子目录。这样做的原因是,命名空间子目录被这一行删除:

    klass = File.basename(file, ".rb").camelize.constantize rescue nil
    

    这是我为保留命名空间子目录所做的:

    file.sub!(File.join(Rails.root, "app", "models"), '')
    file.sub!('.rb', '')
    klass = file.classify.constantize rescue nil
    

    这是修改后的完整解决方案:

      def self.all_polymorphic_types(name)
        @poly_hash ||= {}.tap do |hash|
          Dir.glob(File.join(Rails.root, "app", "models", "**", "*.rb")).each do |file|
            file.sub!(File.join(Rails.root, "app", "models"), '')
            file.sub!('.rb', '')
            klass = file.classify.constantize rescue nil
            next unless klass.ancestors.include?(ActiveRecord::Base)
    
            klass.
              reflect_on_all_associations(:has_many).
              select{ |r| r.options[:as] }.
              each do |reflection|
                (hash[reflection.options[:as]] ||= []) << klass
              end
          end
        end
        @poly_hash[name.to_sym]
      end
    

    现在,该方法会将 /models/test/tensile.rb 正确转换为 Test::Tensile,然后再反映其关联。

    只是一个小小的改进,所有功劳都归于 Harish!

    【讨论】:

      【解决方案4】:

      我创建了一个多态模型类,使用方法“all”来测试它。

      class Profile
        # Return all profile instances
        # For class return use 'ret << i' instead of 'ret << i.all'
        def self.all
          ret = []
          subclasses_of(ActiveRecord::Base).each do |i|
            unless i.reflect_on_all_associations.select{|j| j.options[:as] == :profile}.empty?
              ret << i
            end
          end
          ret.flatten
        end
      
        def self.all_associated
          User.all.map{|u| u.profile }.flatten
        end
      end
      

      这是我的应用设置:

      User < ActiveRecord::Base
        belongs_to :profile, :polymorphic => true
      end
      
      Student < ActiveRecord::Base
        has_one :user, :as => :profile
      end
      

      【讨论】:

      • 要在开发环境中完成这项工作,您必须需要模型文件。
      • 这是一个特殊的类,不是 ActiveRecord 的后代。
      【解决方案5】:

      您应该可以只使用关联的集合:

      model.assignments.all
      

      【讨论】:

      • 这将返回模型实例的所有assignments。我想他想为一个类型返回所有​​的assignments
      • 但是你不能只说 Event.assignments.all 吗?
      • Rails 不添加静态关联助手。所以Event.assignments 会抛出NoMethodError
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-11
      • 2013-04-30
      • 2013-10-23
      • 2011-11-07
      • 1970-01-01
      相关资源
      最近更新 更多