Ruby-on-rails 是什么导致了这个 ActiveRecord::ReadOnlyRecord 错误?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/639171/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is causing this ActiveRecord::ReadOnlyRecord error?
提问by user26270
This follows thisprior question, which was answered. I actually discovered I could remove a join from that query, so now the working query is
这是在此之前的问题之后,已回答。我实际上发现我可以从该查询中删除连接,所以现在工作查询是
start_cards = DeckCard.find :all, :joins => [:card], :conditions => ["deck_cards.deck_id = ? and cards.start_card = ?", @game.deck.id, true]
This appears to work. However, when I try to move these DeckCards into another association, I get the ActiveRecord::ReadOnlyRecord error.
这似乎有效。但是,当我尝试将这些 DeckCard 移动到另一个关联中时,出现 ActiveRecord::ReadOnlyRecord 错误。
Here's the code
这是代码
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
and the relevant Models (tableau are the players cards on the table)
和相关的模型(画面是桌子上的球员牌)
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
I am doing a similar action just after this code, adding DeckCardsto the players hand, and that code is working fine. I wondered if I needed belongs_to :tableauin the DeckCard Model, but it works fine for the adding to player's hand. I do have a tableau_idand hand_idcolumns in the DeckCard table.
在此代码之后,我正在执行类似的操作,将其添加DeckCards到玩家手中,并且该代码运行良好。我想知道我是否需要belongs_to :tableauDeckCard 模型,但它可以很好地添加到玩家的手牌中。我在 DeckCard 表中有一个tableau_id和hand_id列。
I looked up ReadOnlyRecord in the rails api, and it doesn't say much beyond the description.
我在 rails api 中查找了 ReadOnlyRecord,它没有说太多超出描述的内容。
回答by vladr
Rails 2.3.3 and lower
Rails 2.3.3 及更低版本
From the ActiveRecord CHANGELOG(v1.12.0, October 16th, 2005):
来自ActiveRecord CHANGELOG(v1.12.0,2005 年 10 月 16 日):
Introduce read-only records. If you call object.readonly! then it will mark the object as read-only and raise ReadOnlyRecord if you call object.save. object.readonly? reports whether the object is read-only. Passing :readonly => true to any finder method will mark returned records as read-only. The :joins option now implies :readonly, so if you use this option, saving the same record will now fail.Use find_by_sql to work around.
引入只读记录。如果你调用 object.readonly! 然后它会将对象标记为只读并在调用 object.save 时引发 ReadOnlyRecord。对象.只读?报告对象是否为只读。将 :readonly => true 传递给任何 finder 方法会将返回的记录标记为只读。 :joins 选项现在意味着 :readonly,所以如果你使用这个选项,保存相同的记录现在将失败。使用 find_by_sql 来解决。
Using find_by_sqlis not really an alternative as it returns raw row/column data, not ActiveRecords. You have two options:
使用find_by_sql并不是真正的替代方法,因为它返回原始行/列数据,而不是ActiveRecords. 您有两个选择:
- Force the instance variable
@readonlyto false in the record (hack) - Use
:include => :cardinstead of:join => :card
- 强制实例变量
@readonly在记录中为假(hack) - 使用
:include => :card代替:join => :card
Rails 2.3.4 and above
Rails 2.3.4 及更高版本
Most of the above no longer holds true, after September 10 2012:
2012 年 9 月 10 日之后,以上大部分内容不再适用:
- using
Record.find_by_sqlisa viable option :readonly => trueis automatically inferred onlyif:joinswas specified withoutan explicit:selectnoran explicit (or finder-scope-inherited):readonlyoption (see the implementation ofset_readonly_option!inactive_record/base.rbfor Rails 2.3.4, or the implementation ofto_ainactive_record/relation.rband ofcustom_join_sqlinactive_record/relation/query_methods.rbfor Rails 3.0.0)- however,
:readonly => trueis always automatically inferred inhas_and_belongs_to_manyif the join table has more than the two foreign keys columns and:joinswas specified without an explicit:select(i.e. user-supplied:readonlyvalues are ignored -- seefinding_with_ambiguous_select?inactive_record/associations/has_and_belongs_to_many_association.rb.) - in conclusion, unless dealing with a special join table and
has_and_belongs_to_many, then@aaronrustad's answer applies just fine in Rails 2.3.4 and 3.0.0. - do notuse
:includesif you want to achieve anINNER JOIN(:includesimplies aLEFT OUTER JOIN, which is less selective and less efficient thanINNER JOIN.)
- 使用
Record.find_by_sql是一个可行的选择 :readonly => true自动推断仅如果:joins被指定,而不显式:select也不显式(或取景-范围继承):readonly的选项(见实施set_readonly_option!中active_record/base.rb为Rails 2.3.4,或执行to_a中active_record/relation.rb和的custom_join_sql中active_record/relation/query_methods.rb为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)。 - 总之,除非处理特殊的连接表 and
has_and_belongs_to_many,否则@aaronrustad的答案适用于 Rails 2.3.4 和 3.0.0。 - 如果您想实现(意味着 a ,它的选择性和效率低于.) ,请不要使用。
:includesINNER JOIN:includesLEFT OUTER JOININNER JOIN
回答by balexand
Or in Rails 3 you can use the readonly method (replace "..." with your conditions):
或者在 Rails 3 中,您可以使用 readonly 方法(用您的条件替换“...”):
( Deck.joins(:card) & Card.where('...') ).readonly(false)
回答by Aaron Rustad
This might have changed in recent release of Rails, but the appropriate way to solve this problem is to add :readonly => falseto the find options.
这在最近发布的 Rails 中可能有所改变,但解决此问题的适当方法是在查找选项中添加:readonly => false。
回答by bronson
select('*') seems to fix this in Rails 3.2:
select('*') 似乎在 Rails 3.2 中解决了这个问题:
> Contact.select('*').joins(:slugs).where('slugs.slug' => 'the-slug').first.readonly?
=> false
Just to verify, omitting select('*') does produce a readonly record:
只是为了验证,省略 select('*') 确实会产生只读记录:
> Contact.joins(:slugs).where('slugs.slug' => 'the-slug').first.readonly?
=> true
Can't say I understand the rationale but at least it's a quick and clean workaround.
不能说我理解其中的原理,但至少这是一种快速而干净的解决方法。
回答by bronson
Instead of find_by_sql, you can specify a :select on the finder and everything's happy again...
您可以在查找器上指定一个 :select 来代替 find_by_sql,然后一切都会再次变得愉快...
start_cards = DeckCard.find :all,
:select => 'deck_cards.*',
:joins => [:card],
:conditions => ["deck_cards.deck_id = ? and cards.start_card = ?", @game.deck.id, true]
start_cards = DeckCard.find :all,
:select => 'deck_cards.*',
:joins => [:card],
:conditions => ["deck_cards.deck_id = ? and cards.start_card = ?", @game.deck.id, true]
回答by grosser
To deactivate it...
要停用它...
module DeactivateImplicitReadonly
def custom_join_sql(*args)
result = super
@implicit_readonly = false
result
end
end
ActiveRecord::Relation.send :include, DeactivateImplicitReadonly

