Есть ли способ получить коллекцию всех моделей в вашем приложении Rails?Есть ли способ получить коллекцию всех моделей в вашем приложении Rails?
В принципе, я могу сделать, подобного: -
Models.each do |model|
puts model.class.name
end
Есть ли способ получить коллекцию всех моделей в вашем приложении Rails?Есть ли способ получить коллекцию всех моделей в вашем приложении Rails?
В принципе, я могу сделать, подобного: -
Models.each do |model|
puts model.class.name
end
EDIT: Посмотрите на комментарии и другие ответы. Есть более умные ответы, чем этот! Или попробуйте улучшить это как сообщество wiki.
Модели не регистрируются на главном объекте, поэтому нет, у Rails нет списка моделей.
Но вы могли бы еще посмотреть в содержании каталога моделей вашего приложения ...
Dir.foreach("#{RAILS_ROOT}/app/models") do |model_path|
# ...
end
EDIT: Еще одна идея (дикий) будет использовать Ruby, отражение искать для каждого класса, который простирается ActiveRecord ::База. Не знаю, как вы можете перечислить все классы, хотя ...
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
Достаточно смешно , Я пошел по-другому! Я начал смотреть на размышления, но не мог понять, как получить список классов! Сразу после того, как я опубликовал сообщение, у меня был мгновенный момент, и я пошел посмотреть Дир. Спасибо за ответ. Очень признателен. –
Вау! Кудос заработал, а потом кто-то! Спасибо за это, я пошел с предложением Dir и в конечном итоге пришлось сканировать, стирать и использовать заглавные буквы! результирующее имя файла. Однако я думаю, что ваше решение намного элегантнее, поэтому я буду использовать его вместо этого. Если бы я мог проголосовать за тебя снова, я бы! :) –
FYI, я приурочил оба метода просто для удовольствия. Поиск каталогов на порядок выше, чем поиск по классам. Вероятно, это было очевидно, но теперь вы знаете :) –
Это, кажется, работает для меня:
Dir.glob(RAILS_ROOT + '/app/models/*.rb').each { |file| require file }
@models = Object.subclasses_of(ActiveRecord::Base)
Rails загружает только модели, когда они используются, поэтому Dir.glob линия «требует msgstr "все файлы в каталоге моделей.
После того, как у вас есть модели в массиве, вы можете делать то, что вы думаете (например, вид кода):
<% @models.each do |v| %>
<li><%= h v.to_s %></li>
<% end %>
Спасибо bhousel. Я изначально пошел с этим подходом, но в итоге использовал решение, которое Vincent разместил выше, поскольку это означало, что мне не нужно было «моделировать» имя файла (т. Е. Вырезать любое _, заглавное! Каждое слово и затем присоединиться их снова). –
с подкаталогами: '... '/ app/models/**/*. Rb'' – artemave
[Object.subclasses_of] (http://apidock.com/rails/Object/subclasses_of) устарел после v2.3.8. –
Только в случае, если кто-нибудь натыкается на этом, у меня есть другое решение, не полагаясь на показания дирижера или расширяя класс класса ...
ActiveRecord::Base.send :subclasses
Это вернет массив классов.Таким образом, вы можете сделать
ActiveRecord::Base.send(:subclasses).map(&:name)
Почему вы не используете 'ActiveRecord :: Base.subclasses', но должны использовать' send'? Кроме того, кажется, что вам нужно «коснуться» модели до ее появления, например 'c = Category.new', и она появится. В противном случае это не произойдет. –
Я не помню, почему я использовал отправить больше, но, вероятно, была причина. Возможно, на версии rails, которую я использовал в то время, подклассы были частными. Вы были правы в отношении «необходимости прикоснуться». – kikito
В Rails 3 это было изменено на 'ActiveRecord :: Base.descendants' –
На одной строке: Dir['app/models/\*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }
Это хорошо, поскольку в Rails 3 ваши модели не загружаются автоматически по умолчанию, поэтому многие из вышеперечисленных методов не возвратят все возможные модели. Моя перестановка также захватывает модели в плагинах и подкаталогах: 'Dir ['**/models/**/*. Rb']. Map {| f | File.basename (f, '. *'). Camelize.constantize} ' – wbharding
@wbharding Это очень хорошо, но он ошибочно, когда пытается статизировать имена моих тестов модели rspec. ;-) – Ajedi32
@wbharding приятное решение, но оно ломается, когда у вас есть модели с именами –
Я искал способы, чтобы сделать это, и в конечном итоге выбрали этот путь:
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
Это единственный способ получить ВСЕ модели, включая модели двигателей Rails, используемых в приложении. Спасибо за совет! – Andrei
Несколько полезных методов: 'ActiveRecord :: Base.connection.tables.each {| t | begin puts "% s:% d"% [t.humanize, t.classify.constantize.count] rescue nil end} 'Некоторые из моделей могут быть не активированы, поэтому вам нужно его спасти. – Andrei
Адаптация @ Андрея немного: 'model_classes = ActiveRecord :: Base.connection.tables.collect {| t | t.classify.constantize rescue nil} .compact' –
ActiveRecord::Base.connection.tables.map do |model|
model.capitalize.singularize.camelize
end
вернет
["Article", "MenuItem", "Post", "ZebraStripePerson"]
Дополнительная информация Если вы хотите вызвать метод с именем объекта без модели: строка неизвестный метод или переменная ошибки используют этот
model.classify.constantize.attribute_names
Это даст вам все таблицы, но не только модели, так как некоторые таблицы не всегда имеют связанные модели. – courtsimas
Это замечательный момент; Благодарю. – lightyrs
Этот ответ должен считаться некорректным, поскольку это возможно (и распространено в устаревших установках), чтобы настроить имя таблицы как нечто отличное от плюрализованного имени модели. [Этот ответ] (http://stackoverflow.com/a/36277614/476712) дает правильный ответ, даже если настройка отличается от конфигурации по умолчанию. – lorefnon
Module.constants.select { |c| (eval c).is_a?(Class) && (eval c) < ActiveRecord::Base }
throws TypeError: неявное преобразование символа в строку в консоли. – snowangel
ActiveRecord::Base.connection.tables
Также хороший комментарий
Это приведет к вам все таблицы, хотя и не только для моделей, поскольку некоторые таблицы не всегда имеют связанные модели. – courtsimas
Я думаю, @ hnovick-х решение является классным, если у вас нет моделей без таблиц. Это решение будет работать в режиме разработки, а
Мой подход немного отличается, хотя -
ActiveRecord::Base.connection.tables.map{|x|x.classify.safe_constantize}.compact
Классифицировать хорошо должен дать вам имя класса из строки правильно. safe_constantize гарантирует, что вы можете безопасно превратить его в класс без исключения исключения. Это необходимо, если у вас есть таблицы базы данных, которые не являются моделями. компактный, так что любые нули в перечислении удаляются.
Это потрясающе @Aditya Sanghi. Я не знал о 'safe_constantize'. – lightyrs
Для рельсов 2.3.x, используйте: ActiveRecord :: Base.connection.tables.map {| x | x.classify.constantize rescue nil} .compact – iheggie
@iheggie Обычно лучше публиковать это как отдельный ответ, чем редактируя его в существующую почту. – Pokechu22
Весь ответ на Rails 3, 4 и 5:
Если cache_classes
выключен (по умолчанию он выключен в разработке, но в производстве):
Rails.application.eager_load!
Тогда:
ActiveRecord::Base.descendants
Это гарантирует, что все модели в вашем приложении, независимо от того, где они находятся, загружаются, а также любые драгоценные камни, которые вы используете, которые предоставляют модели, также загружаются.
Это также должно работать на классы, которые наследуют от 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' – Ajedi32
Я согласен с Джо Лиссом: это должен быть главный ответ! 2 линии вместо 14 в 7 раз более удивительны. –
только в одной строке:
ActiveRecord::Base.subclasses.map(&:name)
Это не показывает все модели для меня. Не знаю, почему. На самом деле это пара коротких слов. – courtsimas
работал для меня. «немного опоздать, чтобы ответить на все. это займет время. –
Вероятно, перед выполнением в режиме разработки, вероятно, требуется «Rails.application.eager_load!». –
Я хотел бы прокомментировать ответ sj26, который является тот, который я предпочитаю, как я работаю в среде разработки, но я не могу из-за моей молодой репутации , Я понял, что он имел в виду, но, возможно, есть небольшая ошибка: насколько я знаю в среде разработки cache_classes отключен (false), поэтому вам нужно вручную загружать приложение для доступа ко всем моделям.
может проверить этот
@models = ActiveRecord::Base.connection.tables.collect{|t| t.underscore.singularize.camelize}
Вот решение, которое было проверено с комплексным приложением 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 и т.д.
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
Можете ли вы дать некоторое объяснение кода, а также ..? – NREZ
Просто наткнулся на этом, как мне нужно, чтобы напечатать все модели с их атрибутами (построенных на комментарии @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}"}}
Я еще не могу прокомментировать, но я думаю, что sj26 answer должен быть лучшим ответом. Подсказка:
Rails.application.eager_load! unless Rails.configuration.cache_classes
ActiveRecord::Base.descendants
Это сработало для меня. Особая благодарность всем вышеперечисленным сообщениям. Это должно вернуть коллекцию всех ваших моделей.
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 4 (ничего себе они изменили одну или две вещи для бога пользы), я решил добавить мой собственный. Те, которые вызвали ActiveRecord :: Base.connection и вытащили имена таблиц, работали, но не получили результат, который я хотел, потому что я спрятал некоторые модели (в папке внутри приложения/models /), которую я не хотел delete:
def list_models
Dir.glob("#{Rails.root}/app/models/*.rb").map{|x| x.split("/").last.split(".").first.camelize}
end
Я положил это в инициализатор и могу назвать его из любого места. Предотвращает ненужное использование мыши.
Если вы хотите только имена классов:
ActiveRecord::Base.descendants.map {|f| puts f}
Просто запустить его в Rails консоли, ничего больше. Удачи!
EDIT: @ sj26 правильно, вам нужно запустить это первым, прежде чем вы можете называть потомков:
Rails.application.eager_load!
Только то, что я хотел. Спасибо! – sunsations
вызов 'map' с' puts'? Я не понимаю, должен быть 'ActiveRecord :: Base.descendants.map (&: model_name)' –
Вы можете сделать это таким образом, но они будут в одном массиве, а не в строках, в гораздо более удобном для чтения формате. –
Это работает для 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
upvolt для этого Rails.application.eager_load! Идея – equivalent8
предполагая, что все модели в приложении/модели и у вас есть 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
.
EDIT:
Недостатком этого метода является то, что он пропускает модели, которые косвенно наследуются от ActiveRecord (например FictionalBook < Book
). Самый верный способ - Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name)
, хотя он медленный.
Для Rails5 моделей are now subclasses из ApplicationRecord
так, чтобы получить список всех моделей в вашем приложении вы делаете:
ApplicationRecord.descendants.collect { |type| type.name }
Или короче:
ApplicationRecord.descendants.collect(&:name)
Если вы находитесь в режиме разработчика, вы необходимо будет поддерживать модели нагрузки до:
Rails.application.eager_load!
Я просто бросаю этот пример здесь, если кто-то считает его полезным. Решение основано на этом ответе https://stackoverflow.com/a/10712838/473040.
Пусть у вас есть столбец public_uid
, который используется в качестве основного идентификатора внешнего мира (вы можете findjreasons почему вы хотели бы сделать это here)
Теперь говорят, что вы ввели в этом поле на кучу существующих моделей, и теперь вы хотите восстановить все записи, которые еще не установлены. Вы можете сделать это, как этот
# 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
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
Чтобы избежать предварительной нагрузки все Rails, вы можете сделать это:
Dir.glob("#{Rails.root}/app/models/**/*.rb").each {|f| require_dependency(f) }
require_dependency (е) то же самое, что 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 двигатели/Railties, смотрите ответ на [@jaime] (http://stackoverflow.com/questions/516579/is-there-a-way-to-get-a-collection-of-all-the-models-in-your-rails-app/4598129 # 4598129) – Andrei
Не работает на рельсах 5.1 – aks