Bu ActiveRecord :: ReadOnlyRecord hatasına yola açan


203

Bu , cevaplanan bu önceki soruyu takip eder . Aslında ben bu sorgudan birleştirme kaldırabilirsiniz keşfetti, şimdi çalışma sorgusu

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

Bu işe yarıyor gibi görünüyor. Ancak, bu DeckCard'ları başka bir ilişkilendirmeye taşımaya çalıştığımda ActiveRecord :: ReadOnlyRecord hatası alıyorum.

İşte kod

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

ve ilgili Modeller (tablodaki oyuncu kartları)

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

Bu koddan hemen sonra benzer bir işlem yapıyorum DeckCards, oyuncuların eline ekliyorum ve bu kod iyi çalışıyor. belongs_to :tableauDeckCard Modeline ihtiyacım olup olmadığını merak ettim , ama oyuncunun eline eklemek için iyi çalışıyor. DeckCard tablosunda bir tableau_idve hand_idsütunları var .

Ben raylar api ReadOnlyRecord baktı, ve açıklamanın ötesinde çok şey söylemiyor.

Yanıtlar:


283

2.3.3 ve altındaki raylar

Gönderen ActiveRecord CHANGELOG(v1.12.0, 16 Ekim 2005) :

Salt okunur kayıtları tanıtın. Object.readonly'ı çağırırsanız! nesneyi salt okunur olarak işaretler ve object.save öğesini çağırırsanız ReadOnlyRecord öğesini yükseltir. object.readonly? nesnenin salt okunur olup olmadığını bildirir. Passing: readonly => true herhangi bir bulma yöntemine true döndürülen kayıtları salt okunur olarak işaretler. : Joins seçeneği artık: salt okunur olduğundan, bu seçeneği kullanırsanız, aynı kaydı kaydetme işlemi başarısız olur. Geçici çözüm bulmak için find_by_sql kullanın.

find_by_sqlHam satır / sütun verilerini döndürdüğü için kullanmak gerçekten bir alternatif değildir ActiveRecords. İki seçeneğiniz var:

  1. Örnek değişkenini @readonlykayıtta false değerine zorla (hack)
  2. Yerine :include => :cardkullanın:join => :card

2.3.4 ve üstü raylar

Yukarıdakilerin çoğu 10 Eylül 2012'den sonra artık geçerli değil:

  • kullanarak Record.find_by_sql bir uygun bir seçenek
  • :readonly => trueotomatik anlaşılmaktadır sadece eğer :joinsbelirtildi olmadan açık :select ne de (-miras kapsamı bulucu veya) açık bir :readonlyseçeneği (uygulanmasını bakınız set_readonly_option!içinde active_record/base.rbRaylar 2.3.4 için veya uygulanmasını to_aiçinde active_record/relation.rbve arasında custom_join_sqlyer active_record/relation/query_methods.rbRaylar 3.0.0 için)
  • Ancak :readonly => trueher zaman otomatik olarak geliştiği belirlenmiştir has_and_belongs_to_manytablo ikiden fazla yabancı anahtarlar sütunları olan ve katılırsanız :joinsaçık olmadan belirtildi :select(kullanıcı tarafından sağlanan yani :readonlydeğerleri dikkate alınmaz - bakınız finding_with_ambiguous_select?içinde active_record/associations/has_and_belongs_to_many_association.rb.)
  • Sonuç olarak, bir özel ile uğraşan sürece masaya katılmak ve has_and_belongs_to_manyardından @aaronrustad'ın cevabı Raylar 2.3.4 ve 3.0.0 sadece para cezası uygulanır.
  • do not kullanmak :includesbir elde etmek istiyorsanız INNER JOIN( :includesbir ima LEFT OUTER JOINaz seçici ve daha az verimli olan INNER JOIN.)

the include, yapılan sorguların sayısını azaltmada yardımcı olur, bunu bilmiyordum; ama ben bir has_many: Tableau / Deckcards dernek değiştirerek düzeltmeye çalıştı ve şimdi bir 'dernek bulunamadı' msg alıyorum; Bunun için başka bir soru yayınlamam gerekebilir
user26270

@codeman, evet,: include sorgu sayısını azaltacak ve içerilen tabloyu koşul kapsamınıza getirecektir (Rails olmadan kayıtlarınızı salt okunur olarak işaretleyen bir tür örtülü birleştirme, SQL herhangi bir şeyi koklar -İçinde bulduğunuz dahil:: join /: select clauses IIRC
vladr

'Has_many: a, ile =>: b' nin çalışması için B ilişkilendirmesinin de bildirilmesi gerekir, örn. 'Has_many: b; has_many: a,: through =>: b ', umarım senin durumun bu mu?
vladr

6
Bu son sürümlerde değişmiş olabilir, ancak find yöntemi özelliklerinin bir parçası olarak şunu okuyabilirsiniz: readonly => false.
Aaron Rustad

1
Bu yanıt, belirtilen bir custom: join_table ile has_and_belongs_to_many ilişkilendirmeniz varsa da geçerlidir.
Lee

172

Veya Rails 3'te salt okunur yöntemi kullanabilirsiniz ("..." ifadesini koşullarınızla değiştirin):

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

1
Hmmm ... Asciicast'lardaki her iki Railscast'e baktım ve ikisi de işlevden bahsetmedi readonly.
Purplejacket

45

Bu, Rails'in son sürümünde değişmiş olabilir, ancak bu sorunu çözmenin uygun yolu : seçeneklere readonly => false eklemektir .


3
En azından 2.3.4 ile durumun böyle olduğuna inanmıyorum
Olly

2
Hala Rails 3.0.10 ile çalışıyor, işte kendi
kodumdan aşağıdakileri

16

select ('*') bunu Rails 3.2'de düzeltiyor gibi görünüyor:

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

Sadece doğrulamak için select ('*') belirtilmezse salt okunur bir kayıt oluşturulur:

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

Gerekçeyi anladığımı söyleyemem ama en azından hızlı ve temiz bir çözüm.


4
Aynı şey Rails 4'te. Alternatif olarak select(quoted_table_name + '.*')
andorov

1
Bu mükemmel bir bronson'du. Teşekkür ederim.
Yolculuk

Bu işe yarayabilir, ancak kullanmaktan daha karmaşıktırreadonly(false)
Kelvin

5

Find_by_sql yerine, şunu belirtebilirsiniz: bulucuda seçin ve her şey tekrar mutlu ...

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


3

Devre dışı bırakmak için ...

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

3
Maymun yaması kırılgandır - yeni ray versiyonları tarafından kolayca kırılabilir. Başka çözümler olduğu için kesinlikle tavsiye edilemez.
Kelvin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.