这是this先前的问题,已得到回答。我实际上发现我可以从该查询中删除一个连接,所以现在工作查询是
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_id
和hand_id
列。
我在rails api中查找了ReadOnlyRecord,并没有超出描述范围。
答案 0 :(得分:281)
Rails 2.3.3及更低
来自ActiveRecord CHANGELOG
(2005年10月16日v1.12.0):
介绍只读记录。如果你调用object.readonly!然后它会 将对象标记为只读并引发 ReadOnlyRecord如果你打电话 object.save。 object.readonly?报告 对象是否为只读。 传递:readonly =&gt;对任何人都忠诚 finder方法将标记返回 记录为只读。 :加入 选项现在意味着:readonly,所以如果 你使用这个选项,保存相同 记录现在将失败。使用find_by_sql 到处工作。
使用find_by_sql
并不是一种替代方法,因为它返回原始行/列数据,而不是ActiveRecords
。您有两种选择:
@readonly
为false :include => :card
代替:join => :card
Rails 2.3.4及以上
2012年9月10日之后,上述大部分内容都不再成立:
Record.find_by_sql
是一个可行的选项:readonly => true
会自动推断仅如果指定了:joins
没有明确的:select
也显式(或finder-scope-inherited):readonly
选项(请参阅set_readonly_option!
中针对Rails 2.3.4的active_record/base.rb
实现,或to_a
中active_record/relation.rb
的实现1}}和custom_join_sql
中的active_record/relation/query_methods.rb
for Rails 3.0.0):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
。)答案 1 :(得分:169)
或者在Rails 3中,您可以使用readonly方法(将“...”替换为您的条件):
( Deck.joins(:card) & Card.where('...') ).readonly(false)
答案 2 :(得分:44)
这可能在最近发布的Rails中有所改变,但解决此问题的适当方法是添加:readonly =&gt;错误到查找选项。
答案 3 :(得分:15)
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
不能说我理解这个理由,但至少它是一个快速而干净的解决方法。
答案 4 :(得分:5)
而不是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]
答案 5 :(得分:3)
停用它......
module DeactivateImplicitReadonly
def custom_join_sql(*args)
result = super
@implicit_readonly = false
result
end
end
ActiveRecord::Relation.send :include, DeactivateImplicitReadonly