206

これは、回答済みのこの前の質問に続きます。私は実際にそのクエリから結合を削除できることを発見したので、現在の作業クエリは

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し、プレイヤーの手に追加しましたが、そのコードは正常に機能しています。belongs_to :tableauDeckCardモデルで必要かと思ったのですが、プレイヤーの手札に加えるのには問題ありません。DeckCard テーブルに と 列がありますtableau_idhand_id

Rails api で ReadOnlyRecord を調べましたが、説明以上のことは何も言っていません。

4

6 に答える 6

287

Rails 2.3.3 以下

ActiveRecord CHANGELOG(v1.12.0、2005 年 10 月 16 日)から:

読み取り専用レコードを導入します。object.readonly! を呼び出すと、次に、オブジェクトを読み取り専用としてマークし、object.save を呼び出すと ReadOnlyRecord を発生させます。object.readonly? オブジェクトが読み取り専用かどうかを報告します。:readonly => true をファインダ メソッドに渡すと、返されたレコードが読み取り専用としてマークされます。 :joins オプションは :readonly を意味するようになったため、このオプションを使用すると、同じレコードの保存が失敗するようになりました。 回避するには、find_by_sql を使用します。

を使用するfind_by_sqlと、生の行/列データが返されるため、実際には代替手段ではありませんActiveRecords。次の 2 つのオプションがあります。

  1. レコードでインスタンス変数@readonlyを false に強制します (ハック)
  2. :include => :cardの代わりに使用:join => :card

Rails 2.3.4 以降

2012 年 9 月 10 日以降、上記のほとんどは当てはまりません。

  • 使用Record.find_by_sql 実行可能なオプションです
  • :readonly => true明示的または明示的 (またはファインダースコープ継承)オプションなしで指定された場合にのみ、自動的に推論されます ( Rails 2.3.4 のinの実装、またはRails 3.0.0のinおよびinの実装を参照してください)。:joins:select :readonlyset_readonly_option!active_record/base.rbto_aactive_record/relation.rbcustom_join_sqlactive_record/relation/query_methods.rb
  • ただし、結合テーブルに 2 つ以上の外部キー列があり、明示的に指定せずに指定された場合 (つまり、ユーザーが指定した値は無視されます。 を参照してください):readonly => trueは常に自動的に推論されます。has_and_belongs_to_many:joins:select:readonlyfinding_with_ambiguous_select?active_record/associations/has_and_belongs_to_many_association.rb
  • 結論として、特別な結合テーブル and を扱わない限り、has_and_belongs_to_many@aaronrustad答えは Rails 2.3.4 と 3.0.0 で問題なく適用されます。
  • を実現したい場合は使用しないでください(よりも選択性と効率性が低いことを意味します。):includesINNER JOIN:includesLEFT OUTER JOININNER JOIN
于 2009-03-12T18:15:06.923 に答える
172

または、Rails 3 では readonly メソッドを使用できます (「...」を条件に置き換えます)。

( Deck.joins(:card) & Card.where('...') ).readonly(false)
于 2010-08-09T23:50:45.930 に答える
45

これはRailsの最近のリリースで変更された可能性がありますが、この問題を解決する適切な方法は、検索オプションに:readonly=>falseを追加することです。

于 2009-06-07T00:32:46.280 に答える
17

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

理論的根拠を理解しているとは言えませんが、少なくとも迅速でクリーンな回避策です。

于 2013-03-01T22:17:09.960 に答える
5

find_by_sqlの代わりに、ファインダーで:selectを指定すると、すべてが再び幸せになります...

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

于 2009-03-27T00:32:54.397 に答える
3

非アクティブ化するには...

module DeactivateImplicitReadonly
  def custom_join_sql(*args)
    result = super
    @implicit_readonly = false
    result
  end
end
ActiveRecord::Relation.send :include, DeactivateImplicitReadonly
于 2012-08-10T22:31:29.697 に答える