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

mr_*_*urf 194 collections activerecord model ruby-on-rails

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

基本上,我可以这样做: -

Models.each do |model|
  puts model.class.name
end
Run Code Online (Sandbox Code Playgroud)

sj2*_*j26 376

Rails 3,4和5的整个答案是:

如果cache_classes关闭(默认情况下它在开发中关闭,但在生产中关闭):

Rails.application.eager_load!
Run Code Online (Sandbox Code Playgroud)

然后:

ActiveRecord::Base.descendants
Run Code Online (Sandbox Code Playgroud)

这样可以确保应用程序中的所有模型(无论它们在何处)都已加载,并且还会加载任何提供模型的宝石.

这也适用于继承的类ActiveRecord::Base,比如ApplicationRecord在Rails 5中,并且只返回后代的子树:

ApplicationRecord.descendants
Run Code Online (Sandbox Code Playgroud)

如果您想了解更多关于如何做到这一点,检查出的ActiveSupport :: DescendantsTracker.

  • 真棒!这应该是公认的答案.对于在rake任务中使用它的任何人:让你的任务依赖于`:environment`来实现`eager_load!`. (32认同)
  • @ Ajedi32不完整,可以在这些目录之外定义模型,特别是在使用带模型的引擎时.稍微好一点,至少是全部`Rails.paths ["app/models"].现有的目录.渴望加载整个应用程序是一个更完整的答案,并将确保绝对没有任何地方可以定义模型. (5认同)
  • @ Ajedi32再次,不是完整的答案.如果你只想加载模型,那么试试:`Rails.application.paths ["app/models"].eager_load!` (3认同)
  • 我得到了sj26的意思,但也许有一点错误:据我所知,在开发环境中,cache_classes关闭(false),这就是为什么你需要手动加载应用程序来访问所有模型的原因.[在此解释](http://guides.rubyonrails.org/v3.2.13/configuring.html) (2认同)
  • @IbrahimTencer 忽略架构迁移模型并仅返回应用程序的模型,那么您可能想使用“ApplicationRecord.descendants”:-) (2认同)

kik*_*ito 115

万一有人偶然发现这个,我有另一个解决方案,不依赖于读取或扩展Class类......

ActiveRecord::Base.send :subclasses
Run Code Online (Sandbox Code Playgroud)

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

ActiveRecord::Base.send(:subclasses).map(&:name)
Run Code Online (Sandbox Code Playgroud)

  • 在Rails 3中,它已被更改为`ActiveRecord :: Base.descendants` (52认同)
  • 感谢Rails 3提示.对于其他任何人来说,你仍然需要在"ActiveRecord :: Base.descendants"列出它们之前"触摸"模型. (11认同)
  • 为什么不使用`ActiveRecord :: Base.subclasses`但必须使用`send`?此外,似乎你必须在它出现之前"触摸"模型,例如`c = Category.new`它会出现.否则,它不会. (8认同)
  • 您必须使用"发送",因为:子类成员受到保护. (3认同)
  • 从技术上讲,在Rails 3中你有子类*和*后代,它们意味着不同的东西. (3认同)

Vin*_*ert 97

编辑:看看评论和其他答案.有比这更聪明的答案!或者尝试将此作为社区维基进行改进.

模型不会将自己注册到主对象,所以不,Rails没有模型列表.

但您仍然可以查看应用程序的models目录的内容......

Dir.foreach("#{RAILS_ROOT}/app/models") do |model_path|
  # ...
end
Run Code Online (Sandbox Code Playgroud)

编辑:另一个(狂野的)想法是使用Ruby反射来搜索扩展ActiveRecord :: Base的每个类.不知道如何列出所有课程,但...

编辑:为了好玩,我找到了列出所有课程的方法

Module.constants.select { |c| (eval c).is_a? Class }
Run Code Online (Sandbox Code Playgroud)

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

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

如果你也想处理派生类,那么你需要测试整个超类链.我是通过向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
Run Code Online (Sandbox Code Playgroud)

  • 此外,重要的是要注意,通过常量方法搜索模型将不包括自应用程序启动以来未引用的任何内容,因为它仅按需加载模型. (9认同)
  • 仅供参考,我为两种方法计时只是为了好玩.查找目录比通过类搜索快一个数量级.这可能是显而易见的,但现在你知道:) (5认同)
  • 我更喜欢'Kernel.const_get constant_name'到'eval constant_name'. (4认同)
  • Rails 3中不再提供`RAILS_ROOT`.而是使用`Dir.glob(Rails.root.join('app/models/*'))` (3认同)

lig*_*yrs 66

ActiveRecord::Base.connection.tables.map do |model|
  model.capitalize.singularize.camelize
end
Run Code Online (Sandbox Code Playgroud)

将返回

["Article", "MenuItem", "Post", "ZebraStripePerson"]
Run Code Online (Sandbox Code Playgroud)

附加信息如果要在没有模型的情况下调用对象名称上的方法:字符串未知方法或变量错误使用此方法

model.classify.constantize.attribute_names
Run Code Online (Sandbox Code Playgroud)

  • 这将获得所有表格,而不仅仅是模型,因为有些表格并不总是具有关联模型. (7认同)

jai*_*ime 33

我找了办法做到这一点,最后选择了这种方式:

in the controller:
    @data_tables = ActiveRecord::Base.connection.tables

in the view:
  <% @data_tables.each do |dt|  %>
  <br>
  <%= dt %>
  <% end %>
  <br>
Run Code Online (Sandbox Code Playgroud)

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

  • 这对单表继承不起作用. (3认同)
  • 一些有用的方法:`ActiveRecord :: Base.connection.tables.each {| t | begin puts"%s:%d"%[t.humanize,t.classify.constantize.count] rescue nil end}`有些模型可能没有被激活,因此你需要拯救它. (2认同)
  • 适应@ Andrei的一点:`model_classes = ActiveRecord :: Base.connection.tables.collect {| t | t.classify.constantize rescue nil} .compact` (2认同)

Nim*_*mir 29

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

ApplicationRecord.descendants.collect { |type| type.name }
Run Code Online (Sandbox Code Playgroud)

或更短:

ApplicationRecord.descendants.collect(&:name)
Run Code Online (Sandbox Code Playgroud)

如果您处于开发模式,则需要先加载模型:

Rails.application.eager_load!
Run Code Online (Sandbox Code Playgroud)


Adi*_*ghi 22

我认为@hnovick的解决方案很酷,如果你没有无桌面型号.该解决方案也适用于开发模式

我的方法略有不同但是 -

ActiveRecord::Base.connection.tables.map{|x|x.classify.safe_constantize}.compact
Run Code Online (Sandbox Code Playgroud)

classify很适合从字符串中正确地给出类的名称.safe_constantize确保您可以安全地将其转换为类而不会抛出异常.如果您的数据库表不是模型,则需要这样做.紧凑,以便删除枚举中的任何nils.

  • 那真是太棒了@Aditya Sanghi.我不知道'safe_constantize`. (3认同)

Jor*_*ing 21

如果你只想要类名:

ActiveRecord::Base.descendants.map {|f| puts f}
Run Code Online (Sandbox Code Playgroud)

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

编辑:@ sj26是对的,你需要先运行它才能调用后代:

Rails.application.eager_load!
Run Code Online (Sandbox Code Playgroud)


小智 17

这似乎对我有用:

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

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

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

<% @models.each do |v| %>
  <li><%= h v.to_s %></li>
<% end %>
Run Code Online (Sandbox Code Playgroud)


dem*_*mir 15

Rails 6 中Zetiwerk成为默认的代码加载器。

对于急切加载,请尝试:

Zeitwerk::Loader.eager_load_all
Run Code Online (Sandbox Code Playgroud)

然后

ApplicationRecord.descendants
Run Code Online (Sandbox Code Playgroud)


vjt*_*vjt 11

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

  • 这个很好,因为在Rails 3中,默认情况下你的模型不会自动加载,因此上述许多方法都不会返回所有可能的模型.我的排列也捕获了插件和子目录中的模型:`Dir ['**/models/**/*.rb'].map {| f | File.basename(f,'.*').camelize.constantize}` (7认同)
  • @wbharding这很不错,但是当它试图对我的rspec模型测试的名称进行constantize时它出错了.;-) (2认同)

Mar*_*ear 10

ActiveRecord::Base.connection.tables


Adr*_*ian 7

只需一行:

 ActiveRecord::Base.subclasses.map(&:name)
Run Code Online (Sandbox Code Playgroud)

  • 这并没有显示我的所有模型.不知道为什么.事实上,这只是短暂的一小部分. (2认同)
  • 在开发模式下执行之前,可能需要`Rails.application.eager_load!`. (2认同)

pan*_*teo 6

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

Rails.application.eager_load! unless Rails.configuration.cache_classes
ActiveRecord::Base.descendants
Run Code Online (Sandbox Code Playgroud)


nit*_*rma 5

是的,有很多方法可以找到所有模型名称,但是我在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
Run Code Online (Sandbox Code Playgroud)

然后简单地打印这个

@model_array
Run Code Online (Sandbox Code Playgroud)