26 ответов:
EDIT: посмотрите на комментарии и другие ответы. Есть более умные ответы, чем этот! Или попробуйте улучшить это как сообщество wiki.
модели не регистрируются в главном объекте, поэтому нет, Rails не имеет списка моделей.
но вы все равно можете посмотреть содержимое каталога моделей вашего приложения...
Dir.foreach("#{RAILS_ROOT}/app/models") do |model_path| # ... endEDIT: другой (дикой) идеей было бы использовать Ruby reflection для поиска всех классов, которые расширяет ActiveRecord:: Base. Не знаю, как вы можете перечислить все классы...
EDIT: просто для удовольствия, я нашел способ перечислить все классы
Module.constants.select { |c| (eval c).is_a? Class }EDIT: наконец-то удалось перечислить все модели, не глядя на каталоги
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
весь ответ для рельсов 3, 4 и 5:
если
cache_classesвыключен (по умолчанию он выключен в разработке, но включен в производстве):Rails.application.eager_load!затем:
ActiveRecord::Base.descendantsэто гарантирует, что все модели в вашем приложении, независимо от того, где они находятся, загружаются, и любые драгоценные камни, которые вы используете, которые предоставляют модели, также загружаются.
это также должно работать на классы, которые наследуют от
ActiveRecord::Base, какApplicationRecordв Rails 5, и возвращают только это поддерево потомки:ApplicationRecord.descendantsесли вы хотите узнать больше о как это сделано, проверьте ActiveSupport:: DescendantsTracker.
на всякий случай, если кто-то наткнется на это, у меня есть другое решение, не полагаясь на чтение 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"]дополнительная информация если вы хотите вызвать метод по имени объекта без model: string unknown method или variable errors используйте это
model.classify.constantize.attribute_names
Я искал способы сделать это и в конечном итоге выбрал этот путь:
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-это круто, если у вас нет табличных моделей. Это решение будет работать и в режиме разработки
мой подход немного отличается, хотя -
ActiveRecord::Base.connection.tables.map{|x|x.classify.safe_constantize}.compactclassify хорошо должен дать вам имя класса из строки правильно. safe_constantize гарантирует, что вы можете безопасно превратить его в класс, не создавая исключения. Это необходимо в случае, если у вас есть таблицы базы данных, которые не являются моделями. компактируйте так, чтобы все Нили в перечислении были удалены.
Если вы хотите только имена классов:
ActiveRecord::Base.descendants.map {|f| puts f}просто запустите его в консоли Rails, не более того. Удачи вам!
EDIT: @sj26 правильно, вам нужно сначала запустить это, прежде чем вы сможете вызвать потомков:
Rails.application.eager_load!
на Rails5 модели сейчас подклассы на
ApplicationRecordИтак, чтобы получить список всех моделей в вашем приложении вы делаете:ApplicationRecord.descendants.collect { |type| type.name }или короче:
ApplicationRecord.descendants.collect(&:name)если вы находитесь в режиме dev, вам нужно будет нетерпеливо загружать модели до:
Rails.application.eager_load!
Это, кажется, работает для меня:
Dir.glob(RAILS_ROOT + '/app/models/*.rb').each { |file| require file } @models = Object.subclasses_of(ActiveRecord::Base)Rails загружает только модели, когда они используются, поэтому реж.строка glob "требует" все файлы в каталоге models.
Как только у вас есть модели в массиве, вы можете делать то, что вы думали (например, в коде представления):
<% @models.each do |v| %> <li><%= h v.to_s %></li> <% end %>
Я пока не могу комментировать, но думаю sj26 ответ должен быть лучший ответ. Просто подсказка:
Rails.application.eager_load! unless Rails.configuration.cache_classes ActiveRecord::Base.descendants
это работает для рельсы 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
чтобы избежать предварительной загрузки всех рельсов, вы можете сделать это:
Dir.glob("#{Rails.root}/app/models/**/*.rb").each {|f| require_dependency(f) }require_dependency (f) - это то же самое, что
Rails.application.eager_load!использует. Это должно избежать уже необходимых ошибок файлов.затем вы можете использовать все виды решений для перечисления моделей AR, таких как
ActiveRecord::Base.descendants
Да есть много способов, вы можете найти все имена моделей, но то, что я сделал в моем драгоценном камне model_info, это даст вам все модели даже в камни.
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
вот решение, которое было проверено с помощью сложного приложения Rails (один питающий квадрат)
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}"}}
это сработало для меня. Отдельное спасибо всем постам выше. Это должно вернуть коллекцию всех ваших моделей.
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
The
Railsреализует методdescendants, но модели не обязательно когда-либо наследует отActiveRecord::Base, например, класс, который включает модульActiveModel::Modelбудет иметь то же поведение, что и модель, просто не будет связан с таблицей.Итак, дополняя то, что говорят коллеги выше, малейшее усилие сделало бы это:
обезьяна патч класса
ClassРубина: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
Я пробовал так много из этих ответов безуспешно в рельсы 4 (ничего себе они изменили вещь или два для Бога) я решил добавить свой собственный. Те, которые называются ActiveRecord:: Base.соединение и вытащил имена таблиц работали, но не получили желаемого результата, потому что я скрыл некоторые модели (в папке внутри приложения/модели/), которые я не хотел удалять:
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 }'` model_names = results.split("\n")это быстрее, чем
Rails.application.eager_load!или цикл через каждый файл сDir.EDIT:
недостатком этого метода является то, что он пропускает модели, которые косвенно наследуются от ActiveRecord (например,
FictionalBook < Book). Самый верный способ -Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name), хотя это немного медленно.
Я просто привожу этот пример здесь, Если кто-то найдет его полезным. Решение основано на этом ответе https://stackoverflow.com/a/10712838/473040.
Допустим, у вас есть столбец
public_uidкоторый используется в качестве основного идентификатора для внешнего мира (вы можете найти причины, почему вы хотите это сделать здесь)Теперь предположим, что вы ввели это поле в кучу существующих моделей, и теперь вы хотите восстановить все записи, которые еще не установлены. Вы можно сделать вот так
# 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
Comments