Есть ли способ получить коллекцию всех моделей в вашем приложении Rails? - PullRequest
187 голосов
/ 05 февраля 2009

Есть ли способ, которым вы можете получить коллекцию всех Моделей в вашем приложении Rails?

В основном, я могу сделать что-то вроде: -

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

Ответы [ 27 ]

364 голосов
/ 23 мая 2012

Полный ответ для Rails 3, 4 и 5:

Если cache_classes выключен (по умолчанию он выключен в разработке, но включен в работе):

Rails.application.eager_load!

Тогда:

ActiveRecord::Base.descendants

Это гарантирует, что все модели в вашем приложении, независимо от того, где они находятся, загружены, и все используемые вами драгоценные камни, которые предоставляют модели, также загружаются.

Это также должно работать с классами, которые наследуются от ActiveRecord::Base, как ApplicationRecord в Rails 5, и возвращают только это поддерево потомков:

ApplicationRecord.descendants

Если вы хотите узнать больше о , как это делается, посмотрите ActiveSupport :: DescendantsTracker .

113 голосов
/ 12 января 2010

На всякий случай, если кто-нибудь наткнется на это, у меня есть другое решение, не полагаясь на чтение директории или расширение класса Class ...

ActiveRecord::Base.send :subclasses

Это вернет массив классов. Таким образом, вы можете сделать

ActiveRecord::Base.send(:subclasses).map(&:name)
95 голосов
/ 05 февраля 2009

РЕДАКТИРОВАТЬ: Посмотрите на комментарии и другие ответы. Есть более умные ответы, чем этот! Или попробуйте улучшить его как вики сообщества.

Модели не регистрируются в главном объекте, поэтому нет, у Rails нет списка моделей.

Но вы все равно можете посмотреть содержимое каталога моделей вашего приложения ...

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
64 голосов
/ 09 декабря 2011
ActiveRecord::Base.connection.tables.map do |model|
  model.capitalize.singularize.camelize
end

вернется

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

Дополнительная информация Если вы хотите вызвать метод для имени объекта без модели: строка неизвестный метод или переменные ошибки используйте это

model.classify.constantize.attribute_names
31 голосов
/ 04 января 2011

Я искал способы сделать это и в итоге выбрал этот способ:

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

22 голосов
/ 29 марта 2016

Для Rails5 модели теперь являются подклассами из ApplicationRecord, поэтому для получения списка всех моделей в вашем приложении вы должны:

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

или короче:

ApplicationRecord.descendants.collect(&:name)

Если вы находитесь в режиме разработки, вам нужно будет загрузить модели до:

Rails.application.eager_load!
22 голосов
/ 21 мая 2012

Я думаю, что решение @ hnovick будет классным, если у вас нет моделей без таблиц. Это решение будет работать и в режиме разработки

Мой подход немного отличается -

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

classify должен дать вам имя класса из строки правильно . safe_constantize гарантирует, что вы можете безопасно превратить его в класс, не создавая исключения. Это необходимо, если у вас есть таблицы базы данных, которые не являются моделями. компактный, чтобы все нули в перечислении были удалены.

21 голосов
/ 05 января 2014

Если вы хотите только имена классов:

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

Просто запустите его в консоли Rails, не более того. Удачи!

РЕДАКТИРОВАТЬ: @ sj26 правильно, вам нужно сначала запустить это, прежде чем вы можете вызвать потомков:

Rails.application.eager_load!
17 голосов
/ 06 февраля 2009

Мне кажется, это работает:

  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 %>
11 голосов
/ 11 марта 2010

На одной строке: Dir['app/models/\*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...