Rails uygulamanızdaki tüm Modellerin bir koleksiyonunu almanın bir yolu var mı?
Temel olarak, beğenileri yapabilir miyim: -
Models.each do |model|
puts model.class.name
end
Rails uygulamanızdaki tüm Modellerin bir koleksiyonunu almanın bir yolu var mı?
Temel olarak, beğenileri yapabilir miyim: -
Models.each do |model|
puts model.class.name
end
Yanıtlar:
DÜZENLEME: Yorumlara ve diğer yanıtlara bakın. Bundan daha akıllı cevaplar var! Veya bunu topluluk wiki'si olarak geliştirmeye çalışın.
Modeller kendilerini bir ana nesneye kaydetmezler, bu nedenle hayır, Rails modellerin listesine sahip değildir.
Ancak yine de uygulamanızın modeller dizininin içeriğine bakabilirsiniz ...
Dir.foreach("#{RAILS_ROOT}/app/models") do |model_path|
# ...
end
EDIT: Başka (vahşi) bir fikir, ActiveRecord :: Base genişleten her sınıfı aramak için Ruby yansıması kullanmak olacaktır. Tüm sınıfları nasıl listeleyebileceğinizi bilmiyorum ...
EDIT: Sadece eğlence için, tüm sınıfları listelemek için bir yol buldum
Module.constants.select { |c| (eval c).is_a? Class }
EDIT: Sonunda dizinlere bakmadan tüm modelleri listeleme başarılı
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
Eğer türetilmiş sınıfı da ele almak istiyorsanız, tüm üst sınıf zincirini test etmeniz gerekecektir. Class sınıfına bir yöntem ekleyerek yaptım:
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
RAILS_ROOT
artık Rails 3'te mevcut değil. Bunun yerine, kullanınDir.glob(Rails.root.join('app/models/*'))
ActiveRecord::Base
, bu yüzden tüm modelleri yüklerseniz, bunları kolayca yineleyebilirsiniz - aşağıdaki cevabımı görün.
Ray 3, 4 ve 5'in cevabı:
Eğer cache_classes
kapalıysa (varsayılan olarak üretimde üzerinde gelişiminde kapalı, ama):
Rails.application.eager_load!
Sonra:
ActiveRecord::Base.descendants
Bu, uygulamanızdaki tüm modellerin, nerede olduklarına bakılmaksızın, yüklendiğinden ve kullandığınız mücevher sağlayan modellerin de yüklendiğinden emin olmanızı sağlar.
Bu , Rails 5'teki ActiveRecord::Base
gibi miras alan ApplicationRecord
ve yalnızca o torun alt ağacını döndüren sınıflar üzerinde de çalışmalıdır :
ApplicationRecord.descendants
Bunun nasıl yapıldığı hakkında daha fazla bilgi edinmek isterseniz ActiveSupport :: DescendantsTracker'a göz atın .
:environment
için eager_load!
işe.
Rails.application.eager_load!
, sadece modelleri yükleyebilirsiniz:Dir.glob(Rails.root.join('app/models/*')).each do |x| require x end
Rails.paths["app/models"].existent
dizinleri topla . Tüm uygulamayı yüklemek istekli olmak daha eksiksiz bir cevaptır ve modellerin tanımlanması için kesinlikle hiçbir yer kalmamasını sağlayacaktır.
Rails.application.paths["app/models"].eager_load!
Herkesin bu konuda tökezlemesi durumunda, Dir sınıfını okumaya veya Class sınıfını genişletmeye dayanmayan başka bir çözümüm var ...
ActiveRecord::Base.send :subclasses
Bu bir dizi sınıf döndürür. Böylece yapabilirsin
ActiveRecord::Base.send(:subclasses).map(&:name)
ActiveRecord::Base.subclasses
ama kullanmak zorundasın send
? Ayrıca, görünmeden önce modele "dokunmanız" gibi görünüyor, örneğin c = Category.new
ve görünecek. Aksi halde olmayacak.
ActiveRecord::Base.descendants
ActiveRecord::Base.descendants
listelemek için modellere "dokunmanız" gerekir.
ActiveRecord::Base.connection.tables.map do |model|
model.capitalize.singularize.camelize
end
dönecek
["Article", "MenuItem", "Post", "ZebraStripePerson"]
Ek bilgiler Nesne adında model olmadan bir yöntem çağırmak istiyorsanız: string unknown method veya değişken hataları bunu kullanın
model.classify.constantize.attribute_names
ActiveRecord::Base.send :subclasses
- tablo adlarını aramak iyi bir fikirdir. Model adlarının otomatik olarak oluşturulması, belirtildiği gibi sorunlu olabilir.
.capitalize.singularize.camelize
ile değiştirilebilir .classify
.
Bunu yapmanın yollarını aradım ve bu yolu seçtim:
in the controller:
@data_tables = ActiveRecord::Base.connection.tables
in the view:
<% @data_tables.each do |dt| %>
<br>
<%= dt %>
<% end %>
<br>
kaynak: http://portfo.li/rails/348561-how-can-one-list-all-database-tables-from-one-project
ActiveRecord::Base.connection.tables.each{|t| begin puts "%s: %d" % [t.humanize, t.classify.constantize.count] rescue nil end}
Bazı modeller etkinleştirilmeyebilir, bu nedenle onu kurtarmanız gerekir.
model_classes = ActiveRecord::Base.connection.tables.collect{|t| t.classify.constantize rescue nil }.compact
İçin Rails5 modelleri şimdi alt sınıfların arasında ApplicationRecord
yapmanız uygulamanızdaki tüm modellerin listesini almak çok:
ApplicationRecord.descendants.collect { |type| type.name }
Veya daha kısa:
ApplicationRecord.descendants.collect(&:name)
Dev modundaysanız, daha önce yük modelleri istekli olmanız gerekir:
Rails.application.eager_load!
Ben tablo olmadan modelleri yoksa @ hnovick çözüm harika bir çözüm olduğunu düşünüyorum. Bu çözüm geliştirme modunda da işe yarar
Benim yaklaşımım çok farklı olsa da -
ActiveRecord::Base.connection.tables.map{|x|x.classify.safe_constantize}.compact
classify'ın bir dizeden uygun şekilde sınıfın adını vermesi iyi olur . safe_constantize, bir istisna atmadan güvenle bir sınıfa dönüştürmenizi sağlar. Model olmayan veritabanı tablolarınız olması durumunda bu gereklidir. numaralandırmalı böylece numaralandırmadaki nils çıkarılır.
safe_constantize
.
Yalnızca Sınıf adlarını istiyorsanız:
ActiveRecord::Base.descendants.map {|f| puts f}
Sadece Rails konsolunda çalıştırın, başka bir şey değil. İyi şanslar!
EDIT: @ sj26 doğru, torunları aramak için önce bunu çalıştırmanız gerekir:
Rails.application.eager_load!
map
ile puts
? Olması gereken noktayı anlamıyorumActiveRecord::Base.descendants.map(&:model_name)
Bu benim için işe yarıyor gibi görünüyor:
Dir.glob(RAILS_ROOT + '/app/models/*.rb').each { |file| require file }
@models = Object.subclasses_of(ActiveRecord::Base)
Rails modelleri yalnızca kullanıldıklarında yükler, bu nedenle Dir.glob satırı modeller dizinindeki tüm dosyaları "gerektirir".
Bir dizideki modelleri aldıktan sonra, düşündüğünüzü yapabilirsiniz (örneğin, görünüm kodunda):
<% @models.each do |v| %>
<li><%= h v.to_s %></li>
<% end %>
...'/app/models/**/*.rb'
Bir satırda: Dir['app/models/\*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }
Dir['**/models/**/*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }
ActiveRecord::Base.connection.tables
Sadece bir satırda:
ActiveRecord::Base.subclasses.map(&:name)
Rails.application.eager_load!
geliştirme modunda yürütülmeden önce ihtiyacı vardır .
Henüz yorum yapamam, ancak sj26 cevabının en önemli cevap olduğunu düşünüyorum . Sadece bir ipucu:
Rails.application.eager_load! unless Rails.configuration.cache_classes
ActiveRecord::Base.descendants
Evet, tüm model adlarını bulabilmenin birçok yolu var ama benim mücevher model_info'mda yaptığım şey, mücevherlere dahil olan tüm modelleri size verecek.
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
o zaman bunu yazdır
@model_array
Bu Rails 3.2.18 için çalışır
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
Tüm Rayları önceden yüklemekten kaçınmak için şunları yapabilirsiniz:
Dir.glob("#{Rails.root}/app/models/**/*.rb").each {|f| require_dependency(f) }
gereksinim_ bağımlılığı (f), Rails.application.eager_load!
. Bu, gerekli dosya hatalarından kaçınmalıdır.
Ardından AR modellerini listelemek için her türlü çözümü kullanabilirsiniz. ActiveRecord::Base.descendants
İşte karmaşık bir Rails uygulaması (bir güçlendirme Meydanı) ile incelenmiş bir çözüm
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
Bu konudaki cevapların en iyi kısımlarını alır ve bunları en basit ve en kapsamlı çözümde birleştirir. Bu, modellerinizin alt dizinlerde olduğu durumlarda, set_table_name vb. Kullanın.
Tüm modelleri kendi nitelikleriyle (@Aditya Sanghi'nin yorumuna dayanarak) yazdırmam gerektiğinden, bununla karşılaştım:
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}"}}
Bu benim için çalıştı. Yukarıdaki tüm gönderilere özel teşekkürler. Bu, tüm modellerinizin bir koleksiyonunu döndürmelidir.
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
Uygular yöntem descendants
zorunlu hiç devralan, ama modeller ActiveRecord::Base
modülü içerir, örneğin, sınıfActiveModel::Model
bir model olarak aynı davranışı sahip olacak, sadece bir tabloya bağlantılı olacak etmez.
Yukarıdaki meslektaşları söyleyenleri tamamlamak, en ufak bir çaba bunu yapardı:
Class
Ruby sınıfının Maymun Yaması :
class Class
def extends? constant
ancestors.include?(constant) if constant != self
end
end
ve models
ataları içeren yöntem şu şekilde:
Yöntem Module.constants
(yüzeysel olarak) symbols
sabitler yerine bir koleksiyon döndürür , bu nedenle yöntem Array#select
aşağıdaki maymun yaması gibi ikame edilebilir 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
Maymun yaması 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
Ve son olarak, modeller yöntemi
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
Bu cevapların birçoğunu Rails 4'te başarısızlıkla denedim (wow tanrı sakes için bir veya iki şey değiştirdi) Kendi eklememe karar verdim. ActiveRecord :: Base.connection adı verilen ve tablo adlarını çekenler çalıştı ama istediğim sonucu alamadım çünkü istemediğim bazı modelleri (app / models / içindeki bir klasörde) gizledim delete:
def list_models
Dir.glob("#{Rails.root}/app/models/*.rb").map{|x| x.split("/").last.split(".").first.camelize}
end
Bunu bir başlatıcıya koydum ve her yerden çağırabilirim. Gereksiz fare kullanımını önler.
Tüm modellerin uygulama / modellerde olduğunu ve sunucunuzda grep & awk bulunduğunu varsayarsak (vakaların çoğu),
# 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")
Daha hızlı Rails.application.eager_load!
veya her dosya ile döngü Dir
.
DÜZENLE:
Bu yöntemin dezavantajı, dolaylı olarak ActiveRecord'dan (örn. FictionalBook < Book
) Miras alınan modelleri kaçırmasıdır . En Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name)
yavaş yol, biraz yavaş olmasına rağmen.
Birisi faydalı bulursa bu örneği buraya atıyorum. Çözüm bu cevaba dayanmaktadır https://stackoverflow.com/a/10712838/473040 .
Diyelim ki public_uid
dış dünyaya birincil kimlik olarak kullanılan bir sütununuz var (bunu neden yapmak isteyeceğinizin nedenlerini burada bulabilirsiniz )
Şimdi bu alanı mevcut Modeller grubuyla tanıttığınızı ve şimdi henüz ayarlanmamış tüm kayıtları yeniden oluşturmak istediğinizi varsayalım. Bunu böyle yapabilirsin
# 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
şimdi koşabilirsin rake di:public_uids:generate