これは、 this 前の質問に続き、答えられました。私は実際にそのクエリから結合を削除できることを発見したので、今では作業クエリは
start_cards = DeckCard.find :all, :joins => [:card], :conditions => ["deck_cards.deck_id = ? and cards.start_card = ?", @game.deck.id, true]
これは動作するようです。ただし、これらのDeckCardを別の関連付けに移動しようとすると、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_id
列とhand_id
列があります。
Rails apiでReadOnlyRecordを検索しましたが、説明以上のことは言っていません。
Rails 2.3.3以前
ActiveRecord CHANGELOG
から(v1.12.0、2005年10月16日):
読み取り専用レコードを導入します。 object.readonly!を呼び出す場合その後、オブジェクトを読み取り専用としてマークし、object.saveを呼び出すとReadOnlyRecordを発生させます。 object.readonly?オブジェクトが読み取り専用かどうかを報告します。 Finderメソッドに:readonly => trueを渡すと、返されたレコードが読み取り専用としてマークされます。 :joinsオプションは:readonlyを意味するようになったため、このオプションを使用すると、同じレコードの保存は失敗します。 find_by_sqlを使用して回避します。
find_by_sql
の使用は、ActiveRecords
ではなく、生の行/列データを返すため、実際には代替手段ではありません。次の2つのオプションがあります。
@readonly
を強制的にfalseにします(ハック):include => :card
の代わりに:join => :card
を使用しますRails 2.3.4以降
2012年9月10日以降、上記のほとんどは当てはまりません。
Record.find_by_sql
isを使用可能なオプション:readonly => true
は自動的に推測されますonly:joins
が指定された場合without明示的な:select
nor明示的な(またはFinder-scope継承):readonly
オプション( Rails 2.3.4のset_readonly_option!
でのactive_record/base.rb
の実装、またはRails 3.0.0のto_a
でのactive_record/relation.rb
およびcustom_join_sql
でのactive_record/relation/query_methods.rb
の実装: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
の答えはRails 2.3.4および3.0.0でうまく適用されます。:includes
を実現する場合はINNER JOIN
を使用します(:includes
はLEFT OUTER JOIN
を意味します。これはINNER JOIN
よりも選択性が低く、効率が劣ります。)または、Rails 3では、読み取り専用メソッドを使用できます(「...」を条件に置き換えます)。
( Deck.joins(:card) & Card.where('...') ).readonly(false)
これは最近のRailsのリリースで変更された可能性がありますが、この問題を解決する適切な方法は、検索オプションに: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