有没有办法在Rails应用程序中获取所有模型的集合?

有没有一种方法可以在Rails应用程序中获得所有模型的集合?

基本上,我可以做的喜欢: –

Models.each do |model| puts model.class.name end 

编辑:看看评论和其他答案。 有比这更聪明的答案! 或者尝试改进这个社区维基。

模型不注册自己的主对象,所以不,Rails没有模型的列表。

但是您仍然可以查看应用程序模型目录的内容…

 Dir.foreach("#{RAILS_ROOT}/app/models") do |model_path| # ... end 

编辑:另一个(野性)的想法是使用Rubyreflection来search每个扩展ActiveRecord :: Base的类。 不知道如何列出所有的类,但…

编辑:只是为了好玩,我find了列出所有类的方法

 Module.constants.select { |c| (eval c).is_a? Class } 

编辑:最后成功地列出所有模型,而不看目录

 Module.constants.select do |constant_name| constant = eval constant_name if not constant.nil? and constant.is_a? Class and constant.superclass == ActiveRecord::Base constant end end 

如果你也想处理派生类,那么你将需要testing整个超类链。 我通过在Class类中添加一个方法来完成它:

 class Class def extend?(klass) not superclass.nil? and ( superclass == klass or superclass.extend? klass ) end end def models Module.constants.select do |constant_name| constant = eval constant_name if not constant.nil? and constant.is_a? Class and constant.extend? ActiveRecord::Base constant end end end 

Rails 3,4,5的全部答案是:

如果cache_classes处于closures状态(默认情况下它在开发中,但在生产中):

 Rails.application.eager_load! 

然后:

 ActiveRecord::Base.descendants 

这将确保您的应用程序中的所有模型(不pipe它们在哪里)都被加载,并且您正在使用的提供模型的任何gem也将被加载。

这应该也适用于从ActiveRecord::Baseinheritance的类,比如Rails 5中的ApplicationRecord ,并且只返回子树的子树:

 ApplicationRecord.descendants 

如果您想了解更多关于如何完成的信息,请查看ActiveSupport :: DescendantsTracker 。

为了防止任何人在这一个绊脚石,我有另一个解决scheme,不依靠dir阅读或扩展class级…

 ActiveRecord::Base.send :subclasses 

这将返回一个类的数组。 所以你可以这样做

 ActiveRecord::Base.send(:subclasses).map(&:name) 
 ActiveRecord::Base.connection.tables.map do |model| model.capitalize.singularize.camelize end 

将返回

 ["Article", "MenuItem", "Post", "ZebraStripePerson"] 

附加信息如果您想在没有模型的情况下调用对象名称的方法:string未知方法或variables错误,请使用此方法

 model.classify.constantize.attribute_names 

我寻找方法来做到这一点,最终select这种方式:

 in the controller: @data_tables = ActiveRecord::Base.connection.tables in the view: <% @data_tables.each do |dt| %> <br> <%= dt %> <% end %> <br> 

源代码: http : //portfo.li/rails/348561-how-can-one-list-all-database-tables-from-one-project

我认为@ hnovick的解决scheme是一个很酷的,如果你没有无表模型。 这个解决scheme也可以在开发模式下工作

我的方法虽然略微不同 –

 ActiveRecord::Base.connection.tables.map{|x|x.classify.safe_constantize}.compact 

classify是应该从string正确给你的类的名字。 safe_constantize确保您可以安全地将其转换为类,而不会引发exception。 如果您有不是模型的数据库表,这是需要的。 紧凑,以便列举中的任何nils被删除。

这似乎为我工作:

  Dir.glob(RAILS_ROOT + '/app/models/*.rb').each { |file| require file } @models = Object.subclasses_of(ActiveRecord::Base) 

Rails仅在使用时加载模型,因此Dir.glob行“需要”模型目录中的所有文件。

一旦你有了一个数组中的模型,你可以做你在想什么(例如在视图代码中):

 <% @models.each do |v| %> <li><%= h v.to_s %></li> <% end %> 

如果你只想要类名:

 ActiveRecord::Base.descendants.map {|f| puts f} 

只需要在Rails控制台中运行它,仅此而已。 祝你好运!

编辑:@ sj26是正确的,你需要先运行这个之前,你可以调用后代:

 Rails.application.eager_load! 

在一行上: Dir['app/models/\*.rb'].map {|f| File.basename(f, '.*').camelize.constantize } Dir['app/models/\*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }

对于Rails5模型,现在是ApplicationRecord 子类 ,以便获取ApplicationRecord中所有模型的列表:

 ApplicationRecord.descendants.collect { |type| type.name } 

或更短:

 ApplicationRecord.descendants.collect(&:name) 

如果您处于开发模式,则需要在以下时间加载模型:

 Rails.application.eager_load! 

只有一行:

  ActiveRecord::Base.subclasses.map(&:name) 

ActiveRecord::Base.connection.tables

我还不能评论,但我认为sj26答案应该是最好的答案。 只是一个提示:

 Rails.application.eager_load! unless Rails.configuration.cache_classes ActiveRecord::Base.descendants 
 Module.constants.select { |c| (eval c).is_a?(Class) && (eval c) < ActiveRecord::Base } 

这适用于Rails 3.2.18

 Rails.application.eager_load! def all_models models = Dir["#{Rails.root}/app/models/**/*.rb"].map do |m| m.chomp('.rb').camelize.split("::").last end end 

我想评论sj26的答案,这是我在开发环境中所喜欢的那个,但是我不能因为我的年轻的声望。 我知道他的意思,但也许有一个小错误:据我所知,在开发环境中,cache_classesclosures(false),这就是为什么你需要手动加载应用程序来访问所有模型。

这是一个已经被复杂的Rails应用程序(一个供电广场)审查的解决scheme,

 def all_models # must eager load all the classes... Dir.glob("#{RAILS_ROOT}/app/models/**/*.rb") do |model_path| begin require model_path rescue # ignore end end # simply return them ActiveRecord::Base.send(:subclasses) end 

它在这个线程中采用了最好的答案部分,并以最简单和最彻底的解决scheme将它们结合起来。 这个句柄的情况下你的模型在子目录中,使用set_table_name等

刚刚遇到这个,因为我需要打印所有模型的属性(build立在@Aditya Sanghi的评论):

 ActiveRecord::Base.connection.tables.map{|x|x.classify.safe_constantize}.compact.each{ |model| print "\n\n"+model.name; model.new.attributes.each{|a,b| print "\n#{a}"}} 

这对我有效。 特别感谢上面的所有post。 这应该返回所有模型的集合。

 models = [] Dir.glob("#{Rails.root}/app/models/**/*.rb") do |model_path| temp = model_path.split(/\/models\//) models.push temp.last.gsub(/\.rb$/, '').camelize.constantize rescue nil end 

Rails实现了方法的descendants ,但模型不一定从ActiveRecord::Baseinheritance,例如,包含模块ActiveModel::Model将具有与模型相同的行为,只是不会链接到表。

所以补充上面的同事的话,最轻微的努力就是做到这一点:

猴子Ruby Class的补丁类:

 class Class def extends? constant ancestors.include?(constant) if constant != self end end 

以及包括祖先在内的方法models如下:

Module.constants方法返回(表面上)一组symbols ,而不是常量,所以Array#select方法可以像Module这个猴子补丁一样被replace:

 class Module def demodulize splitted_trail = self.to_s.split("::") constant = splitted_trail.last const_get(constant) if defines?(constant) end private :demodulize def defines? constant, verbose=false splitted_trail = constant.split("::") trail_name = splitted_trail.first begin trail = const_get(trail_name) if Object.send(:const_defined?, trail_name) splitted_trail.slice(1, splitted_trail.length - 1).each do |constant_name| trail = trail.send(:const_defined?, constant_name) ? trail.const_get(constant_name) : nil end true if trail rescue Exception => e $stderr.puts "Exception recovered when trying to check if the constant \"#{constant}\" is defined: #{e}" if verbose end unless constant.empty? end def has_constants? true if constants.any? end def nestings counted=[], &block trail = self.to_s collected = [] recursivityQueue = [] constants.each do |const_name| const_name = const_name.to_s const_for_try = "#{trail}::#{const_name}" constant = const_for_try.constantize begin constant_sym = constant.to_s.to_sym if constant && !counted.include?(constant_sym) counted << constant_sym if (constant.is_a?(Module) || constant.is_a?(Class)) value = block_given? ? block.call(constant) : constant collected << value if value recursivityQueue.push({ constant: constant, counted: counted, block: block }) if constant.has_constants? end end rescue Exception end end recursivityQueue.each do |data| collected.concat data[:constant].nestings(data[:counted], &data[:block]) end collected end end 

String猴子补丁。

 class String def constantize if Module.defines?(self) Module.const_get self else demodulized = self.split("::").last Module.const_get(demodulized) if Module.defines?(demodulized) end end end 

最后,模型方法

 def models # preload only models application.config.eager_load_paths = model_eager_load_paths application.eager_load! models = Module.nestings do |const| const if const.is_a?(Class) && const != ActiveRecord::SchemaMigration && (const.extends?(ActiveRecord::Base) || const.include?(ActiveModel::Model)) end end private def application ::Rails.application end def model_eager_load_paths eager_load_paths = application.config.eager_load_paths.collect do |eager_load_path| model_paths = application.config.paths["app/models"].collect do |model_path| eager_load_path if Regexp.new("(#{model_path})$").match(eager_load_path) end end.flatten.compact end 

为了避免预加载所有的Rails,你可以这样做:

 Dir.glob("#{Rails.root}/app/models/**/*.rb").each {|f| require_dependency(f) } 

require_dependency(f)和Rails.application.eager_load! 使用。 这应该避免已经需要的文件错误。

然后你可以使用各种解决scheme列出AR模型,比如ActiveRecord::Base.descendants

是的,有很多方法可以find所有的模型名称,但是我在gem model_info中做的是,它会给你所有的模型,甚至包括在gem中。

 array=[], @model_array=[] Rails.application.eager_load! array=ActiveRecord::Base.descendants.collect{|x| x.to_s if x.table_exists?}.compact array.each do |x| if x.split('::').last.split('_').first != "HABTM" @model_array.push(x) end @model_array.delete('ActiveRecord::SchemaMigration') end 

然后简单地打印这个

 @model_array 
 def load_models_in_development if Rails.env == "development" load_models_for(Rails.root) Rails.application.railties.engines.each do |r| load_models_for(r.root) end end end def load_models_for(root) Dir.glob("#{root}/app/models/**/*.rb") do |model_path| begin require model_path rescue # ignore end end end 

我已经在Rails 4中尝试了很多这样的答案,但是我决定添加自己的答案。 那些调用ActiveRecord :: Base.connection和拉表名称工作,但没有得到我想要的结果,因为我已经隐藏了一些模型(在app / models /里面的一个文件夹),我不想删除:

 def list_models Dir.glob("#{Rails.root}/app/models/*.rb").map{|x| x.split("/").last.split(".").first.camelize} end 

我把它放在初始化器中,可以从任何地方调用它。 防止不必要的鼠标使用。

可以检查这个

 @models = ActiveRecord::Base.connection.tables.collect{|t| t.underscore.singularize.camelize} 

假设所有模型都在应用/模型中,并且您的服务器上有grep&awk(大部分情况),

 # extract lines that match specific string, and print 2nd word of each line results = `grep -r "< ActiveRecord::Base" app/models/ | awk '{print $2}'` model_names = results.split("\n") 

它比Rails.application.eager_load!更快Rails.application.eager_load! 或者用Dir循环每个文件。

编辑:

这种方法的缺点是它错过了从ActiveRecord间接inheritance的模型(例如FictionalBook < Book )。 最可靠的方法是Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name) Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name) ,尽pipe它有点慢。

如果有人认为它有用,我只是把这个例子扔在这里。 解决scheme是基于这个答案https://stackoverflow.com/a/10712838/473040

假设你有一个用作外部世界的主要ID的列public_uid (你可以find原因为什么你想这样做)

现在让我们假设你已经在一堆现有的模型中引入了这个字段,现在你想重新生成所有尚未设置的logging。 你可以这样做

 # lib/tasks/data_integirity.rake namespace :di do namespace :public_uids do desc "Data Integrity: genereate public_uid for any model record that doesn't have value of public_uid" task generate: :environment do Rails.application.eager_load! ActiveRecord::Base .descendants .select {|f| f.attribute_names.include?("public_uid") } .each do |m| m.where(public_uid: nil).each { |mi| puts "Generating public_uid for #{m}#id #{mi.id}"; mi.generate_public_uid; mi.save } end end end end 

你现在可以运行rake di:public_uids:generate