इस ActiveRecord के कारण क्या है :: ReadOnlyRecord त्रुटि?


203

यह इस पूर्व प्रश्न का अनुसरण करता है , जिसका उत्तर दिया गया था। मुझे वास्तव में पता चला कि मैं उस क्वेरी से एक जुड़ाव निकाल सकता हूं, इसलिए अब काम करने वाला प्रश्न है

start_cards = DeckCard.find :all, :joins => [:card], :conditions => ["deck_cards.deck_id = ? and cards.start_card = ?", @game.deck.id, true]  

यह काम करता प्रतीत होता है। हालाँकि, जब मैं इन डेककार्ड्स को किसी अन्य एसोसिएशन में स्थानांतरित करने का प्रयास करता हूं, तो मुझे ActiveRecord :: ReadOnlyRecord त्रुटि मिलती है।

यहाँ कोड है

for player in @game.players 
  player.tableau = Tableau.new
  start_card = start_cards.pop 
  start_card.draw_pile = false
  player.tableau.deck_cards << start_card  # the error occurs on this line
end

और संबंधित मॉडल (झांकी टेबल पर खिलाड़ियों के कार्ड हैं)

class Player < ActiveRecord::Base
  belongs_to :game
  belongs_to :user
  has_one :hand
  has_one :tableau
end

class Tableau < ActiveRecord::Base
  belongs_to :player
  has_many :deck_cards
end  

class DeckCard < ActiveRecord::Base
  belongs_to :card
  belongs_to :deck  
end

मैं इस कोड के ठीक बाद इसी तरह की कार्रवाई कर रहा हूं, DeckCardsखिलाड़ियों के हाथ जोड़ रहा हूं , और यह कोड ठीक काम कर रहा है। मुझे आश्चर्य हुआ कि क्या मुझे belongs_to :tableauडेककार्ड मॉडल में जरूरत है, लेकिन यह खिलाड़ी के हाथ को जोड़ने के लिए ठीक काम करता है। मैं डेककार्ड तालिका में एक tableau_idऔर hand_idकॉलम है।

मैंने रेल एपी में ReadOnlyRecord को देखा, और यह विवरण से परे बहुत कुछ नहीं कहता है।

जवाबों:


283

2.3.3 और निम्नतर

से ActiveRecord CHANGELOG(v1.12.0, 16 अक्टूबर, 2005) :

केवल-पढ़ने के रिकॉर्ड का परिचय दें। यदि आप ऑब्जेक्ट को कहते हैं। तब यह ऑब्जेक्ट को केवल पढ़ने के लिए चिह्नित करेगा और यदि आप ऑब्जेक्ट को कॉल करते हैं तो ReadOnlyRecord बढ़ाएँ। object.readonly? यह रिपोर्ट करता है कि क्या वस्तु केवल पढ़ने के लिए है। पासिंग: आसानी से => किसी भी खोजक विधि के लिए सही है, केवल-पढ़ने के रूप में दिए गए रिकॉर्ड को चिह्नित करेगा। : Joins विकल्प का अर्थ है: आसानी से, इसलिए यदि आप इस विकल्प का उपयोग करते हैं, तो उसी रिकॉर्ड को सहेजना अब विफल हो जाएगा। चारों ओर काम करने के लिए find_by_sql का उपयोग करें।

उपयोग करना find_by_sqlवास्तव में एक विकल्प नहीं है क्योंकि यह कच्ची पंक्ति / स्तंभ डेटा देता है, नहीं ActiveRecords। आपके पास दो विकल्प हैं:

  1. @readonlyरिकॉर्ड चर (हैक) में उदाहरण चर को बाध्य करें
  2. के :include => :cardबजाय का उपयोग करें:join => :card

रेलगाड़ी 2.3.4 और इसके बाद के संस्करण

उपर्युक्त में से अधिकांश अब 10 सितंबर 2012 के बाद सही नहीं हैं:

  • का उपयोग कर Record.find_by_sql रहा है एक व्यवहार्य विकल्प
  • :readonly => trueस्वचालित रूप से मान लिया जाता है केवल अगर :joinsनिर्दिष्ट किया गया था बिना किसी स्पष्ट :select है और न ही कोई स्पष्ट (या खोजक-गुंजाइश-विरासत में मिला है) :readonlyविकल्प (के कार्यान्वयन को देखने set_readonly_option!में active_record/base.rbरेल 2.3.4 के लिए, या के कार्यान्वयन to_aमें active_record/relation.rbऔर की custom_join_sqlमें active_record/relation/query_methods.rbरेल 3.0.0 के लिए)
  • हालांकि, :readonly => trueहमेशा स्वचालित रूप से में मान लिया जाता है has_and_belongs_to_many, तो में शामिल होने तालिका दो से अधिक विदेशी कुंजी स्तंभ होते हैं और :joinsबिना किसी स्पष्ट निर्दिष्ट किया गया था :select(यानी उपयोगकर्ता के आपूर्ति :readonlyमूल्यों अनदेखी कर रहे हैं - यह देखने finding_with_ambiguous_select?में active_record/associations/has_and_belongs_to_many_association.rb।)
  • निष्कर्ष में, जब तक कि एक विशेष ज्वाइन टेबल के साथ काम नहीं किया जाता है has_and_belongs_to_many, और फिर @aaronrustadजवाब रेल के 2.3.4 और 3.0.0 में ठीक लागू होता है।
  • करते नहीं का उपयोग :includesकरता है, तो आप एक हासिल करना चाहते हैं INNER JOIN( :includesएक का अर्थ है LEFT OUTER JOIN, जो कम चयनात्मक और कम से कम कुशल है INNER JOIN।)

: शामिल किए गए प्रश्नों के # को कम करने में सहायक है, मुझे इसके बारे में पता नहीं था; लेकिन मैंने इसे झांकी / डेककार्ड्स एसोसिएशन में बदलकर has_many द्वारा ठीक करने का प्रयास किया: मुझे इसके लिए एक और सवाल पोस्ट करना पड़ सकता है
user26270

@ कोडमैन, हां,: इसमें शामिल प्रश्नों की संख्या कम हो जाएगी और इसमें शामिल तालिका को आपकी स्थिति में लाया जाएगा (एक प्रकार का निहितार्थ बिना रिकॉर्ड के आपके रिकॉर्ड को केवल-पढ़ने के लिए चिह्नित करना, जो इसे SQL के साथ सूँघते ही करता है -अपने खोजने में, सहित: शामिल हों /: चयन करें खंड IIRC
vladr

काम करने के लिए 'has_many: a, through =>: b' के लिए, B एसोसिएशन को भी घोषित किया जाना चाहिए, जैसे 'has_many: b; has_many: a:: through =>: b ', मुझे आशा है कि यह आपका मामला है?
vladr

6
यह हाल के रिलीज में बदल गया है, लेकिन आप केवल जोड़ सकते हैं: आसानी से => झूठी विधि विधि के भाग के रूप में गलत है।
आरोन रुस्तद

1
यह उत्तर तब भी लागू होता है जब आपके पास एक कस्टम के साथ has_and_belongs_to_many एसोसिएशन: join_table निर्दिष्ट होता है।
ली

172

या रेल 3 में आप अपनी शर्तों के साथ (केवल "..." की विधि का उपयोग कर सकते हैं):

( Deck.joins(:card) & Card.where('...') ).readonly(false)

1
हम्म्म ... मैंने उन दोनों रेलसेकस को असिस्का पर देखा, और न ही readonlyफ़ंक्शन का उल्लेख किया ।
पर्पलजेट 17

45

रेल की हालिया रिलीज़ में यह बदल गया हो सकता है, लेकिन इस समस्या को हल करने का उपयुक्त तरीका यह है : खोजने के विकल्पों में आसानी से गलत => गलत जोड़ें ।


3
मेरा मानना ​​है कि यह मामला नहीं है, 2.3.4 के साथ कम से कम
ऑली

2
यह अभी भी रेल 3.0.10 के साथ काम करता है, यहाँ एक गुंजाइश प्राप्त करते समय मेरे अपने कोड से एक उदाहरण है कि एक है: Fundraiser.donatable.readonly (गलत) में शामिल होने के
Houen

16

चयन ('*') रेल 3.2 में इसे ठीक करने के लिए लगता है:

> Contact.select('*').joins(:slugs).where('slugs.slug' => 'the-slug').first.readonly?
=> false

बस सत्यापित करने के लिए, चयन को छोड़ देना ('*') एक आसानी से रिकॉर्ड का उत्पादन करता है:

> Contact.joins(:slugs).where('slugs.slug' => 'the-slug').first.readonly?
=> true

कह नहीं सकता कि मैं औचित्य को समझता हूं, लेकिन कम से कम यह एक त्वरित और स्वच्छ समाधान है।


4
रेल में एक ही बात 4. वैकल्पिक रूप से आप कर सकते हैं select(quoted_table_name + '.*')
andorov

1
वह प्रतिभाशाली ब्रॉनसन था। धन्यवाद।
ट्रिप

यह काम कर सकता है, लेकिन उपयोग करने की तुलना में अधिक जटिल हैreadonly(false)
केल्विन

5

Find_by_sql के बजाय, आप यह निर्दिष्ट कर सकते हैं: खोजकर्ता पर चयन करें और सब कुछ फिर से खुश ...

start_cards = DeckCard.find :all, :select => 'deck_cards.*', :joins => [:card], :conditions => ["deck_cards.deck_id = ? and cards.start_card = ?", @game.deck.id, true]


3

इसे निष्क्रिय करने के लिए ...

module DeactivateImplicitReadonly
  def custom_join_sql(*args)
    result = super
    @implicit_readonly = false
    result
  end
end
ActiveRecord::Relation.send :include, DeactivateImplicitReadonly

3
बंदर-पैचिंग नाजुक है - बहुत आसानी से नए रेल संस्करणों द्वारा टूट गया। निश्चित रूप से अनजाने में दिए गए अन्य उपाय हैं।
केल्विन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.