क्या कोई ऐसा तरीका है जिससे आप अपने रेल एप्लिकेशन में सभी मॉडलों का संग्रह प्राप्त कर सकते हैं?
मूल रूप से, क्या मैं ऐसा कर सकता हूं: -
Models.each do |model|
puts model.class.name
end
क्या कोई ऐसा तरीका है जिससे आप अपने रेल एप्लिकेशन में सभी मॉडलों का संग्रह प्राप्त कर सकते हैं?
मूल रूप से, क्या मैं ऐसा कर सकता हूं: -
Models.each do |model|
puts model.class.name
end
जवाबों:
संपादित करें: टिप्पणियों और अन्य उत्तरों को देखें। इस एक की तुलना में होशियार उत्तर हैं! या सामुदायिक विकि के रूप में इसे बेहतर बनाने का प्रयास करें।
मॉडल खुद को एक मास्टर ऑब्जेक्ट में पंजीकृत नहीं करते हैं, इसलिए नहीं, रेल के पास मॉडल की सूची नहीं है।
लेकिन आप अभी भी अपने आवेदन के मॉडल निर्देशिका की सामग्री में देख सकते हैं ...
Dir.foreach("#{RAILS_ROOT}/app/models") do |model_path|
# ...
end
संपादित करें: ActiveRecord :: बेस को विस्तारित करने वाली हर वर्ग की खोज के लिए रूबी प्रतिबिंब का उपयोग करने के लिए एक और (जंगली) विचार होगा। पता नहीं कैसे आप सभी वर्गों को सूचीबद्ध कर सकते हैं ...
संपादित करें: सिर्फ मनोरंजन के लिए, मुझे सभी वर्गों को सूचीबद्ध करने का एक तरीका मिला
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
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
अब रेल 3 में उपलब्ध नहीं है। इसके बजाय, उपयोगDir.glob(Rails.root.join('app/models/*'))
ActiveRecord::Base
अब के वंश के रूप में पंजीकृत करते हैं, इसलिए यदि आप सभी मॉडलों को उत्सुक करते हैं तो आप उन्हें आसानी से प्रसारित कर सकते हैं - नीचे अपना उत्तर देखें।
रेल 3, 4 और 5 का पूरा उत्तर है:
यदि cache_classes
बंद है (डिफ़ॉल्ट रूप से यह विकास में बंद है, लेकिन उत्पादन में):
Rails.application.eager_load!
फिर:
ActiveRecord::Base.descendants
यह आपके आवेदन में सभी मॉडलों को सुनिश्चित करता है, भले ही वे कहाँ हैं, लोड किए गए हैं, और आपके द्वारा उपयोग किए जा रहे किसी भी रत्न को भी प्रदान किया जाता है।
यह उन वर्गों पर भी काम करना चाहिए ActiveRecord::Base
, जो वंशानुक्रम में आते हैं , जैसे ApplicationRecord
कि रेल 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
Rails.paths["app/models"].existent
निर्देशिका। पूरे आवेदन को लोड करने वाला उत्सुक अधिक पूर्ण उत्तर है और यह सुनिश्चित करेगा कि मॉडल को परिभाषित करने के लिए बिल्कुल कहीं नहीं बचा है।
Rails.application.paths["app/models"].eager_load!
बस अगर कोई इस पर ठोकर खाता है, तो मुझे एक और उपाय मिल गया है, न कि डीआईआर पढ़ने पर भरोसा करना या क्लास क्लास का विस्तार करना ...
ActiveRecord::Base.send :subclasses
यह कक्षाओं की एक सरणी लौटाएगा। तो आप कर सकते हैं
ActiveRecord::Base.send(:subclasses).map(&:name)
ActiveRecord::Base.subclasses
लेकिन उपयोग करना है send
? इसके अलावा, ऐसा लगता है कि आपको मॉडल दिखाने से पहले उसे "स्पर्श" करना होगा, उदाहरण के लिए c = Category.new
और यह दिखाई देगा। अन्यथा, यह नहीं होगा।
ActiveRecord::Base.descendants
ActiveRecord::Base.descendants
वे उन्हें सूचीबद्ध करेंगे।
ActiveRecord::Base.connection.tables.map do |model|
model.capitalize.singularize.camelize
end
वापस होगा
["Article", "MenuItem", "Post", "ZebraStripePerson"]
अतिरिक्त जानकारी यदि आप मॉडल के बिना ऑब्जेक्ट नाम पर एक विधि को कॉल करना चाहते हैं: स्ट्रिंग अज्ञात विधि या चर त्रुटियां इसका उपयोग करती हैं
model.classify.constantize.attribute_names
ActiveRecord::Base.send :subclasses
- तालिका के नामों की तलाश एक अच्छा विचार है। मॉडल के नाम को स्वचालित रूप से उत्पन्न करने में समस्या हो सकती है क्योंकि लॉरफॉन का उल्लेख किया गया है।
.capitalize.singularize.camelize
को प्रतिस्थापित किया जा सकता है .classify
।
मैंने इसे करने के तरीकों की तलाश की और इस तरह से चयन किया:
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
ActiveRecord::Base.connection.tables.each{|t| begin puts "%s: %d" % [t.humanize, t.classify.constantize.count] rescue nil end}
कुछ मॉडल सक्रिय नहीं हो सकते हैं इसलिए आपको इसे बचाने की आवश्यकता है।
model_classes = ActiveRecord::Base.connection.tables.collect{|t| t.classify.constantize rescue nil }.compact
के लिए Rails5 मॉडल हैं अब उपवर्गों की ApplicationRecord
तो आपके एप्लिकेशन में सभी मॉडलों आप कर की सूची प्राप्त करने:
ApplicationRecord.descendants.collect { |type| type.name }
या कम:
ApplicationRecord.descendants.collect(&:name)
यदि आप देव मोड में हैं, तो आपको पहले मॉडल लोड करने की आवश्यकता होगी:
Rails.application.eager_load!
मुझे लगता है कि @ hnovick का समाधान एक अच्छा है यदि आपके पास टेबल-कम मॉडल नहीं हैं। यह समाधान विकास मोड में भी काम करेगा
हालांकि मेरा दृष्टिकोण बिलकुल अलग है -
ActiveRecord::Base.connection.tables.map{|x|x.classify.safe_constantize}.compact
वर्गीकरण को एक स्ट्रिंग से ठीक से कक्षा का नाम देने के लिए अच्छी तरह से माना जाता है । safe_constantize यह सुनिश्चित करता है कि आप बिना किसी अपवाद के इसे सुरक्षित रूप से एक वर्ग में बदल सकते हैं। यदि आपके पास डेटाबेस टेबल है, तो यह आवश्यक नहीं है। कॉम्पैक्ट ताकि एन्यूमरेशन में किसी भी निल्स को हटा दिया जाए।
safe_constantize
।
यदि आप सिर्फ कक्षा के नाम चाहते हैं:
ActiveRecord::Base.descendants.map {|f| puts f}
बस इसे रेल कंसोल में चलाएं, इससे ज्यादा कुछ नहीं। सौभाग्य!
EDIT: @ sj26 सही है, आपको वंशज कहने से पहले इसे पहले चलाना होगा:
Rails.application.eager_load!
map
साथ बुला puts
? मुझे यह बात ActiveRecord::Base.descendants.map(&:model_name)
यह मेरे लिए काम करने लगता है:
Dir.glob(RAILS_ROOT + '/app/models/*.rb').each { |file| require file }
@models = Object.subclasses_of(ActiveRecord::Base)
रेल केवल मॉडल का उपयोग करने पर लोड करती है, इसलिए Dir.glob लाइन को मॉडल निर्देशिका में सभी फ़ाइलों की आवश्यकता होती है।
एक बार जब आपके पास एक सरणी में मॉडल होते हैं, तो आप वह कर सकते हैं जो आप सोच रहे थे (उदाहरण के लिए कोड में):
<% @models.each do |v| %>
<li><%= h v.to_s %></li>
<% end %>
...'/app/models/**/*.rb'
एक लाइन पर: 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
केवल एक पंक्ति में:
ActiveRecord::Base.subclasses.map(&:name)
Rails.application.eager_load!
विकास मोड में निष्पादन से पहले इसकी आवश्यकता है।
मैं अभी तक टिप्पणी नहीं कर सकता, लेकिन मुझे लगता है कि sj26 उत्तर शीर्ष उत्तर होना चाहिए। बस एक संकेत:
Rails.application.eager_load! unless Rails.configuration.cache_classes
ActiveRecord::Base.descendants
हाँ, ऐसे कई तरीके हैं जिनसे आप सभी मॉडल नामों को पा सकते हैं लेकिन मैंने अपने रत्न मॉडल_इनफो में जो किया है, वह आपको सभी मॉडलों को रत्नों में शामिल कर देगा।
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
सभी रेलों को प्री-लोड करने से बचने के लिए, आप यह कर सकते हैं:
Dir.glob("#{Rails.root}/app/models/**/*.rb").each {|f| require_dependency(f) }
आवश्यकता_ निर्भरता (एफ) वही है जो Rails.application.eager_load!
उपयोग करता है। यह पहले से ही आवश्यक फ़ाइल त्रुटियों से बचना चाहिए।
फिर आप एआर मॉडल को सूचीबद्ध करने के लिए सभी तरह के समाधानों का उपयोग कर सकते हैं, जैसे ActiveRecord::Base.descendants
यहाँ एक समाधान है जिसे एक जटिल रेल एप्लिकेशन (एक पावरिंग स्क्वायर) से जोड़ा गया है
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 आदि का उपयोग करें।
बस इस एक के पार आया, क्योंकि मुझे सभी मॉडलों को उनकी विशेषताओं के साथ प्रिंट करने की आवश्यकता है (@ आदित्य संघी की टिप्पणी पर निर्मित):
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}"}}
इसने मेरे लिए काम किया। उपरोक्त सभी पोस्ट के लिए विशेष धन्यवाद। यह आपके सभी मॉडलों का एक संग्रह लौटना चाहिए।
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
लागू विधि 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
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
मैंने इन उत्तरों में से बहुत से लोगों को असफलता की कोशिश की है 4 रेल में (वाह उन्होंने भगवान के लिए एक या दो चीजें बदल दीं) मैंने खुद को जोड़ने का फैसला किया। जो ActiveRecord :: Base.connection कहलाते हैं और तालिका के नामों पर काम करते हैं, लेकिन मुझे जो परिणाम चाहिए, वह नहीं मिला क्योंकि मैंने कुछ मॉडल (ऐप / मॉडल के अंदर एक फ़ोल्डर में) छिपा दिया है, जिसे मैं नहीं चाहता था हटाएँ:
def list_models
Dir.glob("#{Rails.root}/app/models/*.rb").map{|x| x.split("/").last.split(".").first.camelize}
end
मैंने एक इनिशियलाइज़र में डाल दिया है और इसे कहीं से भी कॉल कर सकता है। अनावश्यक माउस-उपयोग को रोकता है।
सभी मॉडलों को एप्लिकेशन / मॉडल में मान लिया गया है और आपके पास अपने सर्वर (मामलों के बहुमत) पर 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
।
संपादित करें:
इस पद्धति का नुकसान यह है कि यह उन मॉडलों को याद करता है जो अप्रत्यक्ष रूप से ActiveRecord (जैसे FictionalBook < Book
) से विरासत में मिले हैं । सबसे सुरक्षित तरीका है Rails.application.eager_load!; ActiveRecord::Base.descendants.map(&:name)
, भले ही यह थोड़े धीमे हो।
मैं सिर्फ इस उदाहरण को यहाँ फेंक रहा हूँ अगर किसी को यह उपयोगी लगता है। समाधान इस उत्तर https://stackoverflow.com/a/10712838/473040 पर आधारित है ।
मान लें कि आपके पास एक कॉलम है public_uid
जिसे बाहरी दुनिया के लिए प्राथमिक आईडी के रूप में उपयोग किया जाता है (आप यह जान सकते हैं कि आप यहां ऐसा क्यों करना चाहते हैं )
अब कहते हैं कि आपने मौजूदा मॉडल के गुच्छा पर इस क्षेत्र को पेश किया है और अब आप उन सभी रिकॉर्डों को फिर से बनाना चाहते हैं जो अभी तक सेट नहीं हैं। आप ऐसा कर सकते हैं
# 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