ActiveRecord :: Relation में ऑब्जेक्ट्स की एक सरणी परिवर्तित करना


104

मेरे पास वस्तुओं की एक सरणी है, चलो इसे ए कहते हैं Indicator। मैं def self.subjectsइस सरणी पर संकेतक वर्ग विधियों ( विविधता, स्कोप, आदि) को चलाना चाहता हूं । जिस तरह से मैं वस्तुओं के एक समूह पर वर्ग विधियों को चलाने के लिए जानता हूं वह उन्हें ActiveRecord :: Relation होना है। इसलिए मैं एक to_indicatorsविधि को जोड़ने का सहारा लेता हूं Array

def to_indicators
  # TODO: Make this less terrible.
  Indicator.where id: self.pluck(:id)
end

कई बार मैं कक्षा के तरीकों के भीतर, परिणामों को फ़िल्टर करने के लिए इनमें से कुछ स्कोपों ​​को श्रृंखलाबद्ध करता हूं। इसलिए, भले ही मैं एक ActiveRecord :: Relation पर एक विधि कहता हूं, मुझे नहीं पता कि उस वस्तु तक कैसे पहुंचा जाए। मैं केवल इसके माध्यम से सामग्री प्राप्त कर सकता हूं all। लेकिन allएक ऐरे है। तो फिर मुझे उस सरणी को ActiveRecord :: Relation में बदलना होगा। उदाहरण के लिए, यह विधियों में से एक है:

all.to_indicators.applicable_for_bank(id).each do |indicator|
  total += indicator.residual_risk_for(id)
  indicator_count += 1 if indicator.completed_by?(id)
end

मुझे लगता है कि यह संक्षेपण दो प्रश्नों के नीचे है।

  1. मैं ऑब्जेक्ट के एक सरणी को ActiveRecord :: Relation में कैसे बदल सकता हूं? अधिमानतः whereप्रत्येक बार किए बिना ।
  2. def self.subjectsActiveRecord :: Relation पर एक प्रकार की विधि चलाते समय , मैं उस ActiveRecord :: Relation ऑब्जेक्ट का उपयोग कैसे करूँ?

धन्यवाद। अगर मुझे कुछ भी स्पष्ट करने की आवश्यकता है, तो मुझे बताएं।


3
यदि आपका एकमात्र कारण उस सरणी को वापस रिश्ते में बदलने की कोशिश करना है, क्योंकि आपको यह मिल गया है .all, तो .scopedएंड्रयू मार्शल के उत्तर की तरह उपयोग करें (हालांकि रेल 4 में यह काम करेगा .all)। यदि आप अपने आप को किसी रिश्ते में एक सरणी मोड़ने की जरूरत महसूस करते हैं, तो आप कहीं गलत हो गए हैं ...
nzifnab

जवाबों:


46

मैं ऑब्जेक्ट के एक सरणी को ActiveRecord :: Relation में कैसे बदल सकता हूं? अधिमानतः प्रत्येक बार जहां बिना किए।

आप किसी ऐरे को ActiveRecord में परिवर्तित नहीं कर सकते हैं :: संबंध चूंकि एक SQL SQL क्वेरी के लिए सिर्फ एक बिल्डर है और इसके तरीके वास्तविक डेटा पर काम नहीं करते हैं।

हालाँकि, यदि आप चाहते हैं कि एक रिश्ता है तो:

  • ActiveRecord 3.x के लिए, कॉल न करें allऔर इसके बजाय कॉल करें scoped, जो एक रिलेशन को वापस देगा जो उन्हीं रिकॉर्ड्स का प्रतिनिधित्व करता है जो allआपको Array में देंगे।

  • ActiveRecord 4.x के लिए, बस कॉल करें all, जो एक संबंध देता है।

def self.subjectsActiveRecord :: Relation पर एक प्रकार की विधि चलाते समय , मैं उस ActiveRecord :: Relation ऑब्जेक्ट का उपयोग कैसे करूँ?

जब विधि को एक संबंध वस्तु पर कहा जाता है, selfतो संबंध है (मॉडल वर्ग के विपरीत इसे परिभाषित किया गया है)।


1
समाधान के बारे में नीचे @Marco Prins देखें।
जस्टिन

क्या के बारे में। 5 रेल पटरियों में पदावनत विधि 5
जसविंदर

@GstjiSaini मैं आपके द्वारा बताए जा रहे सटीक तरीके के बारे में अनिश्चित हूं, कृपया डॉक या स्रोत लिंक प्रदान करें। हालांकि, अगर यह अपदस्थ है, तो बहुत व्यवहार्य समाधान नहीं है क्योंकि यह जल्द ही दूर हो जाएगा।
एंड्रयू मार्शल

और यही कारण है कि आप कर सकते हैं class User; def self.somewhere; where(location: "somewhere"); end; end, तोUser.limit(5).somewhere
डोरियन

यह एकमात्र उत्तर है जो वास्तव में ओपी को प्रबुद्ध कर रहा है। इस सवाल से पता चलता है कि ActiveRecord कैसे काम करता है। @ जस्टिन सिर्फ इस बात को समझने की कमी को पुष्ट कर रहा है कि क्यों वस्तुओं को पास रखने के लिए सिर्फ उन पर नक़ल करना और एक और अनावश्यक क्वेरी का निर्माण करना बुरा है।
james2m

162

आप ऑब्जेक्ट के एक सरणी को arrActiveRecord में कनवर्ट कर सकते हैं :: इस तरह से संबंध (यह मानते हुए कि आप जानते हैं कि ऑब्जेक्ट कौन से वर्ग हैं, जो आप शायद करते हैं)

MyModel.where(id: arr.map(&:id))

whereहालांकि आपको इसका उपयोग करना होगा , यह एक उपयोगी उपकरण है जिसे आपको उपयोग करने के लिए अनिच्छुक नहीं होना चाहिए। और अब आपके पास एक-लाइनर एक सरणी को एक संबंध में परिवर्तित करना है।

map(&:id)केवल अपने आईडी वाले ऑब्जेक्ट को आपके सरणी में बदल देगा। और एक सरणी को एक ऐसे खंड में पारित करना जहां एक SQL कथन उत्पन्न करेगा जिसमें INकुछ ऐसा दिखता है:

SELECT .... WHERE `my_models`.id IN (2, 3, 4, 6, ....

ध्यान रखें कि सरणी का क्रम खो जाएगा - लेकिन चूंकि आपका उद्देश्य केवल इन वस्तुओं के संग्रह पर एक क्लास विधि को चलाने के लिए है , मुझे लगता है कि यह एक समस्या नहीं होगी।


3
अच्छा किया, ठीक वैसा ही जो मुझे चाहिए था। आप मॉडल पर किसी भी विशेषता के लिए इसका उपयोग कर सकते हैं। मेरे लिए पूरी तरह से काम करता है।
1921 को nfriend21

7
जब आप कर सकते हैं तो शाब्दिक एसक्यूएल का निर्माण क्यों करें where(id: arr.map(&:id))? और कड़ाई से बोलते हुए, यह सरणी को किसी संबंध में परिवर्तित नहीं करता है, बल्कि उन आईडी के साथ वस्तुओं के नए उदाहरण (एक बार संबंध का एहसास होने पर) प्राप्त होता है, जो सरणी में पहले से ही स्मृति उदाहरणों की तुलना में भिन्न विशेषता मान हो सकते हैं।
एंड्रयू मार्शल

7
हालांकि यह ऑर्डर खो देता है।
वेलिज़ार हिस्त्रोव

1
@VelizarHristov ऐसा इसलिए है क्योंकि यह अब एक संबंध है, जिसे केवल एक कॉलम द्वारा आदेश दिया जा सकता है, न कि किसी भी तरह से आप चाहते हैं। बड़े डेटा सेट को संभालने के लिए संबंध तेजी से होते हैं, और कुछ ट्रेड-ऑफ होंगे।
मार्को प्रिन्स

8
बहुत अक्षम है! आपने उन वस्तुओं का एक संग्रह बदल दिया है, जिनके पास पहले से ही मेमोरी है, जिन्हें आप एक्सेस करने के लिए डेटाबेस क्वेरी करने जा रहे हैं। मैं उन वर्ग विधियों को फिर से देखना चाहूंगा जिन्हें आप सरणी पर पुनरावृत्त करना चाहते हैं।
james2m

5

खैर, मेरे मामले में, मुझे ऑब्जेक्ट की एक सरणी को ActiveRecord में बदलने की आवश्यकता है :: संबंध के साथ- साथ उन्हें एक विशिष्ट कॉलम (उदाहरण के लिए आईडी) के साथ सॉर्ट करना। चूंकि मैं MySQL का उपयोग कर रहा हूं, इसलिए फ़ील्ड फ़ंक्शन मददगार हो सकता है।

MyModel.where('id in (?)',ids).order("field(id,#{ids.join(",")})") 

एसक्यूएल ऐसा दिखता है:

SELECT ... FROM ... WHERE (id in (11,5,6,7,8,9,10))  
ORDER BY field(id,11,5,6,7,8,9,10)

MySQL फ़ील्ड फ़ंक्शन


इसके एक संस्करण के लिए जो PostgreSQL के साथ काम करता है, इस धागे से आगे नहीं देखें: stackoverflow.com/questions/1309624/…
armchairdj

0

ActiveRecord::Relation डेटाबेस क्वेरी को बांधता है जो डेटाबेस से डेटा को पुनः प्राप्त करता है।

मान लें कि समझ में आता है, हमारे पास एक ही वर्ग की वस्तुओं के साथ सरणी है, तो हम किस क्वेरी के साथ उन्हें बांधते हैं?

जब मैं दौड़ता हूं,

users = User.where(id: [1,3,4,5])
  User Load (0.6ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4, 5)  ORDER BY created_at desc

यहां ऊपर, ऑब्जेक्ट usersलौटाएं Relationलेकिन इसके पीछे डेटाबेस क्वेरी को बांधें और आप इसे देख सकते हैं,

users.to_sql
 => "SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4, 5)  ORDER BY created_at desc"

इसलिए यह उन ActiveRecord::Relationवस्तुओं के सरणी से वापस आना संभव नहीं है जो एसक्यूएल क्वेरी से स्वतंत्र हैं।


0

सबसे पहले, यह एक चांदी की गोली नहीं है। मेरे अनुभव से, मैंने पाया कि संबंध में परिवर्तित करना कभी-कभी विकल्पों की तुलना में आसान होता है। मैं बहुत ही संयम से और केवल उन मामलों में उपयोग करने की कोशिश करता हूं जहां विकल्प अधिक जटिल होगा।

कहा जा रहा है कि यहाँ मेरा समाधान है, मैंने Arrayकक्षा बढ़ाई है

# lib/core_ext/array.rb

class Array

  def to_activerecord_relation
    return ApplicationRecord.none if self.empty?

    clazzes = self.collect(&:class).uniq
    raise 'Array cannot be converted to ActiveRecord::Relation since it does not have same elements' if clazzes.size > 1

    clazz = clazzes.first
    raise 'Element class is not ApplicationRecord and as such cannot be converted' unless clazz.ancestors.include? ApplicationRecord

    clazz.where(id: self.collect(&:id))
  end
end

एक उदाहरण उदाहरण होगा array.to_activerecord_relation.update_all(status: 'finished')। अब मैं इसका उपयोग कहां करूं?

कभी-कभी आपको ActiveRecord::Relationउदाहरण के लिए फ़िल्टर करने की आवश्यकता होती है न कि पूरे किए गए तत्वों को बाहर निकालने के लिए। उन मामलों में सबसे अच्छा है गुंजाइश का उपयोग करें elements.not_finishedऔर आप अभी भी रखेंगे ActiveRecord::Relation

लेकिन कभी-कभी वह स्थिति अधिक जटिल होती है। समाप्त नहीं होने वाले सभी तत्वों को बाहर निकालें, और पिछले 4 हफ्तों में इसका उत्पादन किया गया है और निरीक्षण किया गया है। नए स्कोप बनाने से बचने के लिए आप किसी ऐरे से फ़िल्टर कर सकते हैं और फिर वापस कन्वर्ट कर सकते हैं। ध्यान रखें कि आप अभी भी DB के लिए एक क्वेरी करते हैं, जल्दी क्योंकि यह खोज करता है idलेकिन फिर भी एक क्वेरी है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.