簡體   English   中英

導致此ActiveRecord :: ReadOnlyRecord錯誤的原因是什么?

[英]What is causing this ActiveRecord::ReadOnlyRecord error?

這是在這個先前的問題之后得到回答的。 我實際上發現我可以從該查詢中刪除一個連接,所以現在工作查詢是

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

這似乎有效。 但是,當我嘗試將這些DeckCards移動到另一個關聯時,我得到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添加到玩家手中,並且該代碼工作正常。 我想知道我是否需要在DeckCard模型中使用belongs_to :tableau ,但它適用於添加到玩家手中。 我在DeckCard表中有一個tableau_idhand_id列。

我在rails api中查找了ReadOnlyRecord,並沒有超出描述范圍。

Rails 2.3.3及更低版本

來自ActiveRecord CHANGELOG (2005年10月16日v1.12.0)

引入只讀記錄。 如果你調用object.readonly! 然后它會將對象標記為只讀,如果調用object.save則引發ReadOnlyRecord。 object.readonly? 報告對象是否為只讀。 傳遞:readonly => true到任何finder方法都會將返回的記錄標記為只讀。 :join連接選項現在暗示:readonly,因此如果使用此選項,則保存相同的記錄現在將失敗。 使用find_by_sql來解決。

使用find_by_sql並不是一種替代方法,因為它返回原始行/列數據,而不是ActiveRecords 您有兩種選擇:

  1. 在記錄中強制實例變量@readonly為false(hack)
  2. 使用:include => :card而不是:join => :card

Rails 2.3.4及以上版本

2012年9月10日之后,上述大多數情況不再適用:

  • 使用Record.find_by_sql 一個可行的選擇
  • :readonly => true :joins 情況下自動推斷:joins沒有顯式的情況下指定了:joins :select 也沒有顯式(或finder-scope-in​​herited) :readonly選項(請參閱active_record/base.rbset_readonly_option!對Rails 2.3的實現。如圖4所示,或實施to_aactive_record/relation.rbcustom_join_sqlactive_record/relation/query_methods.rb為Rails 3.0.0)
  • 但是,如果連接表具有多於兩個外鍵列,則:readonly => true總是在has_and_belongs_to_many自動推斷,並且:joins指定了:joins而沒有顯式:select (即用戶提供的:readonly值被忽略 - 請參閱finding_with_ambiguous_select?active_record/associations/has_and_belongs_to_many_association.rb 。)
  • 總之,除非處理特殊的連接表和has_and_belongs_to_many ,否則@aaronrustad的答案在Rails 2.3.4和3.0.0中應用得很好。
  • 不要使用:includes ,如果你想達到一個INNER JOIN:includes意味着LEFT OUTER JOIN ,這是選擇性較差,比效率較低的INNER JOIN )。

或者在Rails 3中,您可以使用readonly方法(將“...”替換為您的條件):

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

這可能在最近發布的Rails中已經改變,但解決此問題的適當方法是在find選項中添加:readonly => false

select('*')似乎在Rails 3.2中解決了這個問題:

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

只是為了驗證,省略select('*')會產生一個只讀記錄:

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

不能說我理解其基本原理,但至少它是一個快速而干凈的解決方法。

而不是find_by_sql,你可以在finder上指定一個:select,一切都很開心......

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

要停用它......

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM