Gibt es eine Möglichkeit, eine Sammlung aller Modelle in Ihrer Rails-App abzurufen?

201

Gibt es eine Möglichkeit, eine Sammlung aller Modelle in Ihrer Rails-App abzurufen?

Grundsätzlich kann ich Folgendes tun: -

Models.each do |model|
  puts model.class.name
end
mr_urf
quelle
1
Wenn Sie alle Modelle einschließlich Modelle von Rails-Motoren / Railties sammeln müssen, lesen Sie die Antwort von @jaime
Andrei
Funktioniert nicht auf Schienen 5.1
aks

Antworten:

98

EDIT: Schauen Sie sich die Kommentare und andere Antworten an. Es gibt klügere Antworten als diese! Oder versuchen Sie, dieses als Community-Wiki zu verbessern.

Modelle registrieren sich nicht bei einem Master-Objekt. Nein, Rails verfügt nicht über die Liste der Modelle.

Sie können aber trotzdem im Inhalt des Modellverzeichnisses Ihrer Anwendung nachsehen ...

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

BEARBEITEN: Eine andere (wilde) Idee wäre, Ruby Reflection zu verwenden, um nach allen Klassen zu suchen, die ActiveRecord :: Base erweitern. Ich weiß nicht, wie Sie alle Klassen auflisten können ...

EDIT: Nur zum Spaß habe ich einen Weg gefunden, alle Klassen aufzulisten

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

BEARBEITEN: Endlich gelang es, alle Modelle aufzulisten, ohne die Verzeichnisse zu betrachten

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

Wenn Sie auch abgeleitete Klassen verarbeiten möchten, müssen Sie die gesamte Oberklassenkette testen. Ich habe dazu der Class-Klasse eine Methode hinzugefügt:

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
Vincent Robert
quelle
6
Zu Ihrer Information, ich habe beide Methoden nur zum Spaß zeitlich festgelegt. Das Nachschlagen der Verzeichnisse ist um eine Größenordnung schneller als das Durchsuchen der Klassen. Das war wahrscheinlich offensichtlich, aber jetzt wissen Sie :)
Edward Anderson
9
Beachten Sie außerdem, dass die Suche nach Modellen über die Konstantenmethoden nichts enthält, auf das seit dem Start der App nicht verwiesen wurde, da die Modelle nur bei Bedarf geladen werden.
Edward Anderson
4
Ich bevorzuge 'Kernel.const_get Konstantenname' gegenüber 'eval Konstantenname'.
Jeremy Weathers
3
RAILS_ROOTist nicht mehr in Rails 3 verfügbar. Verwenden Dir.glob(Rails.root.join('app/models/*'))
Sie
1
Tatsächlich registrieren sich die Modelle selbst als Nachkommen von ActiveRecord::Basejetzt. Wenn Sie also alle Modelle laden möchten, können Sie sie leicht iterieren - siehe meine Antwort unten.
Sj26
393

Die ganze Antwort für die Schienen 3, 4 und 5 lautet:

Wenn deaktiviert cache_classesist (standardmäßig ist es in der Entwicklung deaktiviert, in der Produktion jedoch aktiviert):

Rails.application.eager_load!

Dann:

ActiveRecord::Base.descendants

Auf diese Weise wird sichergestellt, dass alle Modelle in Ihrer Anwendung geladen werden, unabhängig davon, wo sie sich befinden, und dass alle von Ihnen verwendeten Edelsteine, die Modelle bereitstellen, ebenfalls geladen werden.

Dies sollte auch für Klassen funktionieren ActiveRecord::Base, die wie ApplicationRecordin Rails 5 erben und nur den Teilbaum der Nachkommen zurückgeben:

ApplicationRecord.descendants

Wenn Sie möchten mehr darüber wissen , wie dies geschehen ist, überprüfen Active :: DescendantsTracker .

sj26
quelle
33
Genial! Dies sollte die akzeptierte Antwort sein. Für alle diese in einer Harke Aufgabe mit: Machen Sie Ihre Aufgabe hängt von :environmentfür die eager_load!an der Arbeit.
Jo Liss
1
Oder, als etwas schnellere Alternative zu Rails.application.eager_load!, können Sie einfach die Modelle laden:Dir.glob(Rails.root.join('app/models/*')).each do |x| require x end
Ajedi32
5
@ Ajedi32, das nicht vollständig ist, können Modelle außerhalb dieser Verzeichnisse definiert werden, insbesondere wenn Engines mit Modellen verwendet werden. Etwas besser, zumindest alle Rails.paths["app/models"].existentVerzeichnisse glob . Das eifrige Laden der gesamten Anwendung ist eine vollständigere Antwort und stellt sicher, dass für die Definition von Modellen absolut nichts mehr übrig ist.
Sj26
2
Ich habe verstanden, was sj26 bedeutet, aber vielleicht gibt es einen kleinen Fehler: Soweit ich weiß, ist cache_classes in der Entwicklungsumgebung deaktiviert (false). Deshalb müssen Sie die Anwendung manuell laden, um auf alle Modelle zugreifen zu können. hier erklärt
Masciugo
3
@ Ajedi32 nochmal, nicht die vollständige Antwort. Wenn Sie nur Modelle laden möchten, versuchen Sie:Rails.application.paths["app/models"].eager_load!
sj26
119

Nur für den Fall, dass jemand über diese stolpert, habe ich eine andere Lösung, ohne mich auf das direkte Lesen oder Erweitern der Klassenklasse zu verlassen ...

ActiveRecord::Base.send :subclasses

Dies gibt ein Array von Klassen zurück. So können Sie dann tun

ActiveRecord::Base.send(:subclasses).map(&:name)
Kikito
quelle
8
warum benutzt du nicht, musst es ActiveRecord::Base.subclassesaber benutzen send? Es scheint auch so, als müssten Sie das Modell "berühren", bevor es beispielsweise c = Category.newangezeigt wird und angezeigt wird. Sonst wird es nicht.
Unpolarität
52
In Rails 3 wurde dies geändert inActiveRecord::Base.descendants
Tobias Cohen
3
Sie müssen "send" verwenden, da das Mitglied:: subclasses geschützt ist.
Kevin Rood
11
Danke für den Rails 3 Tipp. Für alle anderen, die mitkommen, müssen Sie die Modelle noch "berühren", bevor ActiveRecord::Base.descendantssie aufgelistet werden.
NFM
3
Technisch gesehen haben Sie in Rails 3 Unterklassen und Nachkommen, die unterschiedliche Bedeutungen haben.
Sj26
67
ActiveRecord::Base.connection.tables.map do |model|
  model.capitalize.singularize.camelize
end

wird zurückkehren

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

Zusätzliche Informationen Wenn Sie eine Methode für den Objektnamen ohne Modell aufrufen möchten: Zeichenfolge unbekannte Methode oder Variablenfehler verwenden Sie diese

model.classify.constantize.attribute_names
Lightyrs
quelle
8
Dadurch erhalten Sie jedoch alle Tabellen, nicht nur die Modelle, da einigen Tabellen nicht immer Modelle zugeordnet sind.
Courtsimas
Diese Antwort sollte als falsch angesehen werden, da es möglich (und in älteren Setups üblich) ist, den Namen der Tabelle so zu konfigurieren, dass er nicht der pluralisierte Name des Modells ist. Diese Antwort gibt die richtige Antwort, auch wenn das Setup von der Standardkonfiguration abweicht.
Lorefnon
In einigen Fällen funktioniert dies besser als ActiveRecord::Base.send :subclasses- das Suchen nach Tabellennamen ist eine gute Idee. Das automatische Generieren der Modellnamen kann problematisch sein, wie bereits erwähnt.
Tilo
.capitalize.singularize.camelizekann ersetzt werden .classify.
Maxim
34

Ich suchte nach Möglichkeiten, dies zu tun, und entschied mich schließlich für diesen Weg:

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

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

Quelle: http://portfo.li/rails/348561-how-can-one-list-all-database-tables-from-one-project

jaime
quelle
1
Nur so kann ich ALLE Modelle erhalten, einschließlich der Modelle der in der App verwendeten Rails-Engines. Danke für den Tipp!
Andrei
2
Einige nützliche Methoden: ActiveRecord::Base.connection.tables.each{|t| begin puts "%s: %d" % [t.humanize, t.classify.constantize.count] rescue nil end}Einige Modelle sind möglicherweise nicht aktiviert, daher müssen Sie sie retten.
Andrei
2
@ Andrei ein bisschen anpassen: model_classes = ActiveRecord::Base.connection.tables.collect{|t| t.classify.constantize rescue nil }.compact
Max Williams
30

Für Rails5 sind Modelle jetzt Unterklassen von ApplicationRecord, um eine Liste aller Modelle in Ihrer App zu erhalten, die Sie ausführen :

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

Oder kürzer:

ApplicationRecord.descendants.collect(&:name)

Wenn Sie sich im Entwicklungsmodus befinden, müssen Sie Modelle unbedingt laden, bevor Sie:

Rails.application.eager_load!
Nimir
quelle
1
Ich gehe davon aus, dass dies voraussetzt, dass Klassen bereits geladen sind und in einer Entwicklungsumgebung mit aktiviertem Autoloading unvollständige Ergebnisse liefert. Ich werde nicht ablehnen, aber vielleicht sollte dies in der Antwort erwähnt werden.
Lorefnon
Fahrpreis genug, Aktualisierung
Nimir
Ich bin auf Rails 6.0.2 und die eifrige Ladung! hat die Nachkommenmethode nicht dazu gebracht, etwas anderes als ein leeres Array zurückzugeben.
jgomo3
23

Ich denke, die Lösung von @ hnovick ist cool, wenn Sie keine Modelle ohne Tisch haben. Diese Lösung würde auch im Entwicklungsmodus funktionieren

Mein Ansatz ist jedoch subtil anders -

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

classify soll Ihnen den Namen der Klasse aus einer Zeichenfolge richtig geben . safe_constantize stellt sicher, dass Sie es sicher in eine Klasse verwandeln können, ohne eine Ausnahme auszulösen. Dies ist erforderlich, wenn Sie Datenbanktabellen haben, die keine Modelle sind. kompakt, so dass alle Nullen in der Aufzählung entfernt werden.

Aditya Sanghi
quelle
3
Das ist großartig @Aditya Sanghi. Ich wusste nichts davon safe_constantize.
Lightyrs
Verwenden Sie für Rails 2.3.x: ActiveRecord :: Base.connection.tables.map {| x | x.classify.constantize Rescue nil} .compact
iheggie
@iheggie Es ist im Allgemeinen besser, dies als separate Antwort zu veröffentlichen, als es in den vorhandenen Beitrag zu bearbeiten.
Pokechu22
danke, ich fand deine Antwort am besten für mich geeignet #adiya
illusionist
21

Wenn Sie nur die Klassennamen möchten:

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

Führen Sie es einfach in der Rails-Konsole aus, sonst nichts. Viel Glück!

EDIT: @ sj26 ist richtig, Sie müssen dies zuerst ausführen, bevor Sie Nachkommen aufrufen können:

Rails.application.eager_load!
Jordan Michael Rushing
quelle
Genau das, was ich wollte. Danken!
Sonnen am
anrufen mapmit puts? Ich verstehe nicht, was der Punkt sein sollteActiveRecord::Base.descendants.map(&:model_name)
Nuno Costa
Sie können dies auf diese Weise tun, aber sie befinden sich in einem einzelnen Array anstelle von Zeile für Zeile in einem viel einfacher zu lesenden Format.
Jordan Michael Rushing
17

Das scheint bei mir zu funktionieren:

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

Rails lädt Modelle nur, wenn sie verwendet werden. Daher "benötigt" die Dir.glob-Zeile alle Dateien im Modellverzeichnis.

Sobald Sie die Modelle in einem Array haben, können Sie das tun, was Sie gedacht haben (z. B. im Ansichtscode):

<% @models.each do |v| %>
  <li><%= h v.to_s %></li>
<% end %>
bhousel
quelle
Danke bhousel. Ich habe mich ursprünglich für diesen Ansatz entschieden, aber letztendlich die Lösung verwendet, die Vincent oben veröffentlicht hat, da dies bedeutete, dass ich nicht auch den Dateinamen "modellieren" musste (dh jedes _ entfernen, jedes Wort groß schreiben und dann beitreten) die schon wieder).
mr_urf
mit Unterverzeichnissen:...'/app/models/**/*.rb'
Artemave
Object.subclasses_of ist nach v2.3.8 veraltet.
David J.
11

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

vjt
quelle
7
Dies ist hilfreich, da Ihre Modelle in Rails 3 standardmäßig nicht automatisch geladen werden, sodass viele der oben genannten Methoden nicht alle möglichen Modelle zurückgeben. Meine Permutation erfasst auch Modelle in Plugins und Unterverzeichnissen:Dir['**/models/**/*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }
wbharding
2
@wbharding Das ist ziemlich nett, aber es tritt ein Fehler auf, wenn versucht wird, die Namen meiner rspec-Modelltests zu konstituieren. ;-)
Ajedi32
@wbharding schöne Lösung, aber es bricht, wenn Sie Modelle mit Namensraum haben
Marcus Mansur
10

ActiveRecord::Base.connection.tables

Mark Locklear
quelle
Ein nettes Follow-up ist auch <Tabellenname> .Spaltennamen, um alle Spalten in der Tabelle aufzulisten. Also für Ihre Benutzertabelle würden Sie User.column_names
Mark Locklear
Dadurch erhalten Sie jedoch alle Tabellen, nicht nur die Modelle, da einigen Tabellen nicht immer Modelle zugeordnet sind.
Courtsimas
7

In nur einer Zeile:

 ActiveRecord::Base.subclasses.map(&:name)
Adrian
quelle
2
Das zeigt mir nicht alle Modelle. Nicht sicher warum. Es ist in der Tat ein paar kurz.
Courtsimas
1
arbeitete für mich. 'nur ein bisschen spät, um das zu beantworten. Gib der Sache Zeit.
boulder_ruby
2
Es wird wahrscheinlich Rails.application.eager_load!vor der Ausführung im Entwicklungsmodus benötigt.
denis.peplin
7

Ich kann noch keinen Kommentar abgeben , aber ich denke, die Antwort von sj26 sollte die beste Antwort sein. Nur ein Hinweis:

Rails.application.eager_load! unless Rails.configuration.cache_classes
ActiveRecord::Base.descendants
panteo
quelle
6

Mit Rails 6 wurde Zetiwerk zum Standard-Code-Loader.

Versuchen Sie zum eifrigen Laden:

Zeitwerk::Loader.eager_load_all

Dann

ApplicationRecord.descendants
demir
quelle
5

Ja, es gibt viele Möglichkeiten, wie Sie alle Modellnamen finden können, aber was ich in meinem Gem model_info getan habe, ist, dass Sie alle Modelle erhalten, die sogar in den Gems enthalten sind.

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

dann einfach ausdrucken

@model_array
Nitanshu Verma
quelle
3

Dies funktioniert für 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
ryan0
quelle
upvolt für das Rails.application.eager_load! Idee
Äquivalent8
3

Um zu vermeiden, dass alle Rails vorgeladen werden, können Sie Folgendes tun:

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

require_dependency (f) ist das gleiche, das Rails.application.eager_load!verwendet wird. Dies sollte bereits erforderliche Dateifehler vermeiden.

Dann können Sie alle Arten von Lösungen verwenden, um AR-Modelle aufzulisten, wie z ActiveRecord::Base.descendants

John Owen Chile
quelle
2
Module.constants.select { |c| (eval c).is_a?(Class) && (eval c) < ActiveRecord::Base }
Naveed
quelle
löst TypeError aus: Keine implizite Konvertierung von Symbol in String in der Konsole.
Schneeangel
1

Hier ist eine Lösung, die mit einer komplexen Rails-App (der Powering Square) überprüft wurde.

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

Es nimmt die besten Teile der Antworten in diesem Thread und kombiniert sie in der einfachsten und gründlichsten Lösung. Dies behandelt Fälle, in denen sich Ihre Modelle in Unterverzeichnissen befinden, verwenden Sie set_table_name usw.

Pascal-Louis Perez
quelle
1

Ich bin gerade auf dieses Modell gestoßen, da ich alle Modelle mit ihren Attributen drucken muss (basierend auf dem Kommentar von @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}"}}
gouravtiwari21
quelle
1

Das hat bei mir funktioniert. Besonderer Dank geht an alle oben genannten Beiträge. Dies sollte eine Sammlung aller Ihrer Modelle zurückgeben.

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
Kevin
quelle
1

Das Railsimplementiert die Methode descendants, aber Modelle erben nicht unbedingt jemals von ActiveRecord::Basebeispielsweise der Klasse, die das Modul enthältActiveModel::Model , haben das gleiche Verhalten wie ein Modell, werden jedoch nicht mit einer Tabelle verknüpft.

Wenn man also die obigen Aussagen ergänzt, würde dies die geringste Anstrengung bewirken:

Affenfleck der Klasse Classdes Rubins:

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

und die Methode models, einschließlich der Vorfahren, wie folgt:

Die Methode Module.constantsgibt (oberflächlich) eine Sammlung von symbolsKonstanten anstelle von Konstanten zurück, sodass die Methode Array#selectwie folgt ersetzt werden kann 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

Affenbeet von 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

Und schließlich die Modellmethode

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
rplaurindo
quelle
1
Dir.foreach("#{Rails.root.to_s}/app/models") do |model_path|
  next unless model_path.match(/.rb$/)
  model_class = model_path.gsub(/.rb$/, '').classify.constantize
  puts model_class
end

Dadurch erhalten Sie alle Modellklassen, die Sie für Ihr Projekt haben.

Sieger
quelle
0
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
Abdul
quelle
0

Ich habe so viele dieser Antworten in Rails 4 erfolglos ausprobiert (wow, sie haben ein oder zwei Dinge geändert, um Himmels willen), dass ich beschlossen habe, meine eigenen hinzuzufügen. Diejenigen, die ActiveRecord :: Base.connection aufgerufen und die Tabellennamen abgerufen haben, haben funktioniert, aber nicht das gewünschte Ergebnis erzielt, da ich einige Modelle (in einem Ordner in app / models /) versteckt habe, die ich nicht wollte löschen:

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

Ich habe das in einen Initialisierer gesteckt und kann es von überall aufrufen. Verhindert unnötigen Mausgebrauch.

boulder_ruby
quelle
0

kann dies überprüfen

@models = ActiveRecord::Base.connection.tables.collect{|t| t.underscore.singularize.camelize}
Arvind
quelle
0

Angenommen, alle Modelle befinden sich in App / Modellen und Sie haben grep & awk auf Ihrem Server (in den meisten Fällen).

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

Es ist schneller als Rails.application.eager_load!oder durchläuft jede Datei mit Dir.

BEARBEITEN:

Der Nachteil dieser Methode besteht darin, dass Modelle fehlen, die indirekt von ActiveRecord erben (z FictionalBook < Book. B. ). Der sicherste Weg ist Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name), obwohl es ein bisschen langsam ist.

konyak
quelle
0

Ich werfe dieses Beispiel nur hier, wenn jemand es nützlich findet. Die Lösung basiert auf dieser Antwort https://stackoverflow.com/a/10712838/473040 .

Angenommen, Sie haben eine Spalte public_uid, die als primäre ID für die Außenwelt verwendet wird (Gründe, warum Sie dies tun möchten, finden Sie hier ).

Angenommen, Sie haben dieses Feld in einer Reihe vorhandener Modelle eingeführt und möchten nun alle noch nicht festgelegten Datensätze neu generieren. Sie können das so machen

# 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

Sie können jetzt laufen rake di:public_uids:generate

Äquivalent8
quelle