Czy istnieje sposób na uzyskanie kolekcji wszystkich modeli w aplikacji Rails?

Czy istnieje sposób, aby uzyskać kolekcję wszystkich modeli w aplikacji Rails?

W zasadzie mogę zrobić coś takiego: -

Models.each do |model|
  puts model.class.name
end
Author: royhowie, 2009-02-05

26 answers

EDIT: spójrz na komentarze i inne odpowiedzi. Są mądrzejsze odpowiedzi niż ta! Lub spróbuj poprawić ten jako wiki społeczności.

Modele nie rejestrują się w obiekcie głównym, więc nie, Rails nie ma listy modeli.

Ale nadal możesz zajrzeć do zawartości katalogu models swojej aplikacji...

Dir.foreach("#{RAILS_ROOT}/app/models") do |model_path|
  # ...
end

EDIT: innym (dzikim) pomysłem byłoby użycie Ruby reflection do wyszukiwania wszystkich klas rozszerzających ActiveRecord::Base. Nie wiem, jak możesz wymienić wszystkie klasy...

EDIT: dla Zabawy znalazłem sposób na listę wszystkich klas

Module.constants.select { |c| (eval c).is_a? Class }

EDIT: W końcu udało się wymienić wszystkie modele bez patrzenia na katalogi

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

Jeśli chcesz obsługiwać również klasę pochodną, musisz przetestować cały łańcuch superclass. Zrobiłem to dodając metodę do klasy:

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
 90
Author: Vincent Robert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-17 16:03:59

Cała odpowiedź dla Rails 3, 4 i 5 brzmi:

Jeśli cache_classes jest wyłączone (domyślnie jest wyłączone w rozwoju, ale w produkcji):

Rails.application.eager_load!

Wtedy:

ActiveRecord::Base.descendants

To sprawia, że wszystkie modele w aplikacji, niezależnie od tego, gdzie się znajdują, są ładowane, a wszelkie klejnoty, których używasz, które dostarczają modele, są również ładowane.

Powinno to również działać na klasach dziedziczących z ActiveRecord::Base, Jak ApplicationRecord W Rails 5, i zwracać tylko ten podzbiór Potomków:

ApplicationRecord.descendants

If you ' d aby dowiedzieć się więcej o Jak to się robi, sprawdź ActiveSupport::DescendantsTracker .

 347
Author: sj26,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-28 22:54:19

Na wypadek, gdyby ktoś się przy tym potykał, mam inne rozwiązanie, nie polegające na czytaniu dir lub rozszerzaniu klasy...

ActiveRecord::Base.send :subclasses

Zwróci tablicę klas. Więc możesz zrobić

ActiveRecord::Base.send(:subclasses).map(&:name)
 112
Author: kikito,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-14 22:17:29
ActiveRecord::Base.connection.tables.map do |model|
  model.capitalize.singularize.camelize
end

Powróci

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

Informacje dodatkowe Jeżeli chcesz wywołać metodę na nazwie obiektu bez modelu: string unknown metoda lub błędy zmiennej użyj tego

model.classify.constantize.attribute_names
 56
Author: lightyrs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-04 00:45:52

Szukałem sposobów na to i skończyło się na tym, że wybrałem ten sposób:

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

in the view:
  <% @data_tables.each do |dt|  %>
  <br>
  <%= dt %>
  <% end %>
  <br>

Źródło: http://portfo.li/rails/348561-how-can-one-list-all-database-tables-from-one-project

 29
Author: jaime,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-04 20:30:47

Myślę, że rozwiązanie @hnovick jest fajne, jeśli nie masz modeli bez tabeli. To rozwiązanie będzie działać również w trybie deweloperskim

Moje podejście jest jednak subtelnie inne -

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

Klasyfikacja jest dobrze powinna dać nazwę klasy z łańcucha poprawnie . safe_constantize zapewnia, że możesz bezpiecznie przekształcić ją w klasę bez rzucania WYJĄTKÓW. Jest to potrzebne w przypadku, gdy masz tabele bazy danych, które nie są modelami. kompaktowy, tak aby każdy nils w wyliczenia są usuwane.

 22
Author: Aditya Sanghi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-21 07:27:02

Jeśli chcesz tylko nazwy klas:

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

Po prostu uruchom go w konsoli Rails, nic więcej. Powodzenia!

EDIT: @sj26 ma rację, musisz najpierw to uruchomić zanim będziesz mógł wywołać Potomków:

Rails.application.eager_load!
 20
Author: Jordan Michael Rushing,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-12 15:11:38

Dla rails5 Modele są teraz podklasami z ApplicationRecord więc aby uzyskać listę wszystkich modeli w aplikacji, wykonaj:

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

Lub krótszy:

ApplicationRecord.descendants.collect(&:name)

Jeśli jesteś w trybie dev, musisz najpierw załadować modele:

Rails.application.eager_load!
 18
Author: Nimir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-22 06:52:59

To chyba działa dla mnie:

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

Rails ładuje tylko modele, gdy są używane, więc Dir.glob line "wymaga" wszystkich plików w katalogu models.

Gdy masz modele w tablicy, możesz zrobić to, o czym myślałeś (np. w kodzie widoku):

<% @models.each do |v| %>
  <li><%= h v.to_s %></li>
<% end %>
 17
Author: bhousel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-06 16:49:19

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

 11
Author: vjt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-25 21:06:11

ActiveRecord::Base.connection.tables

 8
Author: Lumbee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-28 03:26:23

W jednej linijce:

 ActiveRecord::Base.subclasses.map(&:name)
 7
Author: Adrian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-19 14:08:58

Nie mogę jeszcze skomentować, ale myślę, że odpowiedź sj26 powinna być najlepszą odpowiedzią. Tylko podpowiedź:

Rails.application.eager_load! unless Rails.configuration.cache_classes
ActiveRecord::Base.descendants
 6
Author: panteo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:41

To działa dla 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
 3
Author: ryan0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-26 22:34:02

Aby uniknąć wstępnego załadowania wszystkich szyn, możesz to zrobić:

Dir.glob("#{Rails.root}/app/models/**/*.rb").each {|f| require_dependency(f) }

Require_dependency (f) jest tym samym, czego używa Rails.application.eager_load!. Powinno to uniknąć już wymaganych błędów plików.

Następnie możesz użyć wszelkiego rodzaju rozwiązań do listy modeli AR, takich jak ActiveRecord::Base.descendants

 3
Author: John Owen Chile,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-28 15:48:42
Module.constants.select { |c| (eval c).is_a?(Class) && (eval c) < ActiveRecord::Base }
 2
Author: Naveed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-27 12:14:33

Tak Jest wiele sposobów na znalezienie wszystkich nazw modeli , ale to, co zrobiłem w moim klejnocie model_info, da Ci wszystkie modele nawet zawarte w klejnocie.

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

Następnie po prostu wydrukuj to

@model_array
 2
Author: nitanshu verma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-23 11:28:45

Oto rozwiązanie, które zostało sprawdzone za pomocą złożonej aplikacji Rails (tej zasilającej kwadrat)

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

Pobiera najlepsze części odpowiedzi w tym wątku i łączy je w najprostsze i najbardziej dokładne rozwiązanie. Obsługuje przypadki, w których twoje modele znajdują się w podkatalogach, używa set_table_name itp.

 1
Author: Pascal-Louis Perez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-10 12:41:12

Właśnie natknąłem się na ten, ponieważ muszę wydrukować wszystkie modele z ich atrybutami (zbudowany na komentarzu @ 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}"}}
 1
Author: gouravtiwari21,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-23 06:24:36

To mi pomogło. Specjalne podziękowania dla wszystkich postów powyżej. To powinno zwrócić kolekcję wszystkich modeli.

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
 1
Author: Kevin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-13 23:02:44

Rails implementuje metodę descendants, ale modele niekoniecznie dziedziczą z ActiveRecord::Base, na przykład klasa, która zawiera moduł ActiveModel::Model będzie miała takie samo zachowanie jak model, tylko nie będzie powiązana z tabelą.

Więc uzupełniając to, co mówią koledzy powyżej, najmniejszy wysiłek zrobiłby to:

Monkey Patch of class Class Of The Ruby:

class Class
  def extends? constant
    ancestors.include?(constant) if constant != self
  end
end

I metoda models, w tym przodków, jak to:

Metoda Module.constants zwraca (powierzchownie) zbiór symbols, zamiast stałych, więc metoda {[12] } może być podstawiona tak, jak ta mała łata 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

Monkey patch of 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

I wreszcie metoda modeli

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
 1
Author: rplaurindo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-12 21:17:19
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
 0
Author: Abdul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-22 05:29:45

Próbowałem tak wiele z tych odpowiedzi bezskutecznie w Rails 4 (wow zmienili coś lub dwa na litość boską) postanowiłem dodać własne. Te, które nazwały ActiveRecord:: Base.połączenie i wyciągnięte nazwy tabel działały, ale nie otrzymałem pożądanego wyniku, ponieważ ukryłem niektóre modele (w folderze wewnątrz app/models/), których nie chciałem usuwać:

def list_models
  Dir.glob("#{Rails.root}/app/models/*.rb").map{|x| x.split("/").last.split(".").first.camelize}
end

Umieściłem to w inicjalizatorze i mogę zadzwonić z dowolnego miejsca. Zapobiega niepotrzebnemu używaniu myszy.

 0
Author: boulder_ruby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-14 22:25:07

Można to sprawdzić

@models = ActiveRecord::Base.connection.tables.collect{|t| t.underscore.singularize.camelize}
 0
Author: Arvind,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-28 03:25:42

Zakładając, że wszystkie modele są w app / models i masz grep & awk na serwerze (większość przypadków),

# 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")

It it faster than Rails.application.eager_load! or looping through each file with Dir.

EDIT:

Wadą tej metody jest to, że pomija ona modele, które pośrednio dziedziczą z ActiveRecord (np. FictionalBook < Book). Najpewniejszym sposobem jest Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name), mimo że jest trochę powolny.

 0
Author: konyak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-08 22:54:54

Rzucam ten przykład tutaj, jeśli ktoś uzna to za przydatne. Rozwiązanie opiera się na tej odpowiedzi https://stackoverflow.com/a/10712838/473040 .

Powiedzmy, że masz kolumnę public_uid, która jest używana jako główny identyfikator świata zewnętrznego (możesz znaleźć jreasons, dlaczego chcesz to zrobić tutaj )

Teraz powiedzmy, że wprowadziłeś to pole na kilku istniejących modelach i teraz chcesz zregenerować wszystkie rekordy, które nie są jeszcze ustawione. Możesz to zrobić tak

# 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

Możesz teraz uruchomić rake di:public_uids:generate

 0
Author: equivalent8,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:47