Answers:
编辑:看评论和其他答案。有比这更聪明的答案!或尝试将其改进为社区Wiki。
模型不会将自己注册到主对象,因此,Rails没有模型列表。
但是您仍然可以在应用程序的models目录中查找内容。
Dir.foreach("#{RAILS_ROOT}/app/models") do |model_path|
# ...
end
编辑:另一个(疯狂的)想法是使用Ruby反射来搜索扩展ActiveRecord :: Base的每个类。虽然不知道如何列出所有课程...
编辑:只是为了好玩,我找到了列出所有课程的方法
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
如果您也想处理派生类,那么您将需要测试整个超类链。我通过向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_ROOT
在Rails 3中不再可用。而是使用Dir.glob(Rails.root.join('app/models/*'))
ActiveRecord::Base
现在的后代,因此,如果您渴望加载所有模型,则可以轻松地对其进行迭代-请参见下面的答案。
Rails 3、4和5的全部答案是:
如果cache_classes
处于关闭状态(默认情况下,它在开发中处于关闭状态,但在生产中处于打开状态):
Rails.application.eager_load!
然后:
ActiveRecord::Base.descendants
这样可以确保应用程序中的所有模型(无论它们在何处)都被加载,并且还可以加载您使用的提供模型的所有gem。
这也应该适用于继承自的类ActiveRecord::Base
,例如ApplicationRecord
在Rails 5中,并且仅返回后代的子树:
ApplicationRecord.descendants
如果您想了解更多关于如何做到这一点,检查出的ActiveSupport :: DescendantsTracker。
:environment
的eager_load!
工作。
Rails.application.eager_load!
,您可以加载模型:Dir.glob(Rails.root.join('app/models/*')).each do |x| require x end
Rails.paths["app/models"].existent
目录。急于加载整个应用程序是一个更完整的答案,并将确保绝对没有地方可以定义模型。
Rails.application.paths["app/models"].eager_load!
万一有人偶然发现这个,我有另一种解决方案,不依靠dir读取或扩展Class类。
ActiveRecord::Base.send :subclasses
这将返回一个类数组。所以你可以做
ActiveRecord::Base.send(:subclasses).map(&:name)
ActiveRecord::Base.subclasses
却必须使用send
?同样,例如,您似乎必须“触摸”模型,然后模型c = Category.new
才会显示出来。否则,不会。
ActiveRecord::Base.descendants
ActiveRecord::Base.descendants
才能列出它们。
ActiveRecord::Base.connection.tables.map do |model|
model.capitalize.singularize.camelize
end
将返回
["Article", "MenuItem", "Post", "ZebraStripePerson"]
附加信息如果要在对象名称上调用方法而不使用model:string未知方法或变量错误,请使用此方法
model.classify.constantize.attribute_names
ActiveRecord::Base.send :subclasses
- 更好地使用-查找表名是一个好主意。如lorefnon所述,自动生成模型名称可能会出现问题。
.capitalize.singularize.camelize
可以替换为.classify
。
我寻找了实现此目的的方法,最终选择了这种方法:
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
ActiveRecord::Base.connection.tables.each{|t| begin puts "%s: %d" % [t.humanize, t.classify.constantize.count] rescue nil end}
某些模型可能未激活,因此您需要进行补救。
model_classes = ActiveRecord::Base.connection.tables.collect{|t| t.classify.constantize rescue nil }.compact
现在,对于Rails5模型是的子类,ApplicationRecord
要获取您应用中所有模型的列表,请执行以下操作:
ApplicationRecord.descendants.collect { |type| type.name }
或更短:
ApplicationRecord.descendants.collect(&:name)
如果您处于开发人员模式,则需要在以下情况下急于加载模型:
Rails.application.eager_load!
我认为@hnovick的解决方案很不错,如果您没有无表模型。该解决方案也将在开发模式下工作
我的方法虽然有所不同-
ActiveRecord::Base.connection.tables.map{|x|x.classify.safe_constantize}.compact
classify应该从字符串中正确地给您类的名称。safe_constantize确保您可以安全地将其转换为类,而不会引发异常。如果您有不是模型的数据库表,则需要这样做。紧凑,以便删除枚举中的所有零。
safe_constantize
。
如果只需要类名:
ActiveRecord::Base.descendants.map {|f| puts f}
只需在Rails控制台中运行它,仅此而已。祝好运!
编辑:@ sj26是正确的,您需要先运行此命令,然后才能调用后代:
Rails.application.eager_load!
map
给puts
?我不明白重点是ActiveRecord::Base.descendants.map(&:model_name)
这似乎为我工作:
Dir.glob(RAILS_ROOT + '/app/models/*.rb').each { |file| require file }
@models = Object.subclasses_of(ActiveRecord::Base)
Rails仅在使用模型时才加载它们,因此Dir.glob行“需要” models目录中的所有文件。
将模型放入数组后,您可以做您想做的事情(例如,在视图代码中):
<% @models.each do |v| %>
<li><%= h v.to_s %></li>
<% end %>
...'/app/models/**/*.rb'
一行上: Dir['app/models/\*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }
Dir['**/models/**/*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }
ActiveRecord::Base.connection.tables
仅一行:
ActiveRecord::Base.subclasses.map(&:name)
Rails.application.eager_load!
在开发模式下执行之前可能需要它。
是的,有很多方法可以找到所有模型名称,但是我在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
Module.constants.select { |c| (eval c).is_a?(Class) && (eval c) < ActiveRecord::Base }
这是一个经过复杂的Rails应用程序审核的解决方案(一个为Square提供动力的应用程序)
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
它采用了该主题中答案的最佳部分,并将它们组合成最简单,最彻底的解决方案。这可以处理您的模型位于子目录中,使用set_table_name等的情况。
刚遇到这一点,因为我需要打印所有带有属性的模型(建立在@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}"}}
该Rails
实现实现的方法descendants
,但模型不一定要继承自ActiveRecord::Base
例如包含模块的类ActiveModel::Model
将具有与模型相同的行为,只是不会链接到表。
因此,对上面的同事所说的做些补充,只要付出一点点努力就可以做到:
Class
Ruby 类的Monkey Patch :
class Class
def extends? constant
ancestors.include?(constant) if constant != self
end
end
以及models
包括祖先在内的方法,如下所示:
该方法Module.constants
(表面上)返回的集合symbols
,而不是常量,因此,Array#select
可以像下面的猴子补丁那样替换该方法Module
:
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
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
我把它放在一个初始化器中,可以在任何地方调用它。防止不必要的鼠标使用。
假设所有模型都在应用程序/模型中,并且您的服务器上有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!
或循环遍历每个文件更快 Dir
。
编辑:
这种方法的缺点是它会丢失从ActiveRecord间接继承的模型(例如FictionalBook < Book
)。最可靠的方法是Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name)
,即使有点慢。
如果有人发现它有用,我只是在这里举这个例子。解决方案基于此答案https://stackoverflow.com/a/10712838/473040。
假设您有一列public_uid
用作外界的主要ID(您可以在这里找到为什么要这样做的理由)
现在,假设您在一堆现有模型上引入了此字段,现在您想重新生成所有尚未设置的记录。你可以这样
# 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