Ce cauzează această eroare ActiveRecord :: ReadOnlyRecord?

voturi
198

Aceasta urmează această chestiune prealabilă, la care au răspuns. De fapt , am descoperit că pot elimina o alăturare de la acea interogare, asa ca acum interogarea de lucru este

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

Acest lucru pare să funcționeze. Cu toate acestea, atunci când încerc să se mute aceste DeckCards într-o altă asociație, am obține eroarea ActiveRecord :: ReadOnlyRecord.

Iată codul

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

și modelele relevante (tableau sunt cărțile de jucători pe masa)

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

Fac o acțiune similară , imediat după acest cod, adăugând DeckCardsla mâna jucătorilor, și că codul este de lucru bine. M - am întrebat dacă am nevoie belongs_to :tableauîn DeckCard Modelul, dar funcționează bine pentru adăugarea la mâna jucătorului. Am o tableau_idși hand_idcoloanele din tabel DeckCard.

M-am uitat în sus ReadOnlyRecord în api șine, și nu spune mult dincolo de descriere.

Întrebat 12/03/2009 la 14:28
sursa de către utilizator
În alte limbi...                            


6 răspunsuri

voturi
275

Din ActiveRecord CHANGELOG(v1.12.0, 16 octombrie 2005) :

Prezentați - read-only înregistrări. Dacă apelați object.readonly! atunci acesta va marca obiectul ca read-only și ridica ReadOnlyRecord dacă apelați object.save. object.readonly? rapoarte dacă obiectul este read-only. Pase: = numai citire> true la orice metodă de căutare va marca înregistrările returnate ca readonly. The: Opțiunea acum presupune alătură: numai citire, așa că , dacă utilizați această opțiune, economisind aceeași înregistrare nu va reuși acum. Utilizați find_by_sql pentru a lucra în jurul valorii.

Utilizarea find_by_sqlnu este cu adevărat o alternativă , deoarece returnează date rând / coloană prime, nu ActiveRecords. Aveți două opțiuni:

  1. Forța de variabila instanță @readonlyla fals în înregistrarea (hack)
  2. Utilizați în :include => :cardloc de:join => :card

Septembrie 2010 UPDATE

Cele mai multe dintre cele de mai sus nu mai este valabil. Astfel, în Rails 2.3.4 și 3.0.0:

  • folosind Record.find_by_sql este o opțiune viabilă
  • :readonly => trueeste dedus în mod automat numai dacă a :joinsfost specificat fără o explicită și :select nici o explicită (sau Finder-domeniul de aplicare-moștenit) :readonlyopțiunea ( a se vedea implementarea set_readonly_option!în special active_record/base.rbpentru șinele 2.3.4, sau punerea în aplicare a to_aîn active_record/relation.rbși custom_join_sqlîn special active_record/relation/query_methods.rbpentru șinele 3.0.0)
  • Cu toate acestea, :readonly => trueeste întotdeauna dedusă în mod automat în has_and_belongs_to_manycazul în care join tabelul are mai mult decât cele două coloane chei străine și a :joinsfost specificat fără explicit :select( de exemplu , furnizate de utilizator :readonlyvalori sunt ignorate - a se vedea finding_with_ambiguous_select?în active_record/associations/has_and_belongs_to_many_association.rb.)
  • în concluzie, cu excepția cazului în care se ocupă cu o specială se alăture masă și has_and_belongs_to_many, atunci @aaronrustade răspunsul se aplică foarte bine în Rails 2.3.4 și 3.0.0.
  • nu , nu utilizați , :includesdacă doriți să realizeze un INNER JOIN( :includesimplică o LEFT OUTER JOIN, care este mai puțin selectivă și mai puțin eficientă decât INNER JOIN.)
Publicat 12/03/2009 la 17:15
sursa de către utilizator

voturi
5

În loc de find_by_sql, puteți specifica: selectați pe vizor și totul e din nou fericit ...

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

Publicat 26/03/2009 la 23:32
sursa de către utilizator

voturi
43

Acest lucru s - ar fi schimbat în eliberarea recentă de Rails, dar modul adecvat pentru a rezolva această problemă este de a adăuga : = numai citire> false la opțiunile Find.

Publicat 06/06/2009 la 23:32
sursa de către utilizator

voturi
168

Sau în Rails 3 puteți utiliza metoda numai pentru citire (înlocuiți „...“ cu condițiile dumneavoastră):

( Deck.joins(:card) & Card.where('...') ).readonly(false)
Publicat 09/08/2010 la 22:50
sursa de către utilizator

voturi
3

Pentru ao dezactiva ...

module DeactivateImplicitReadonly
  def custom_join_sql(*args)
    result = super
    @implicit_readonly = false
    result
  end
end
ActiveRecord::Relation.send :include, DeactivateImplicitReadonly
Publicat 10/08/2012 la 21:31
sursa de către utilizator

voturi
15

selectați ( „*“) pare să stabilească acest lucru în Rails 3.2:

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

Doar pentru a verifica, omițând selectați ( '*') nu produce un record numai de citire:

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

Nu se poate spune că înțeleg raționamentul, dar cel puțin este o soluție rapidă și curată.

Publicat 01/03/2013 la 20:17
sursa de către utilizator

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more