是啥导致了这个 ActiveRecord::ReadOnlyRecord 错误?
Posted
技术标签:
【中文标题】是啥导致了这个 ActiveRecord::ReadOnlyRecord 错误?【英文标题】:What is causing this ActiveRecord::ReadOnlyRecord error?是什么导致了这个 ActiveRecord::ReadOnlyRecord 错误? 【发布时间】:2010-10-12 22:56:20 【问题描述】:这是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,它并没有超出描述。
【问题讨论】:
【参考方案1】:Rails 2.3.3 及更低版本
来自ActiveRecord CHANGELOG
(v1.12.0,2005 年 10 月 16 日):
引入只读记录。如果你调用 object.readonly!然后它会 将对象标记为只读并引发 ReadOnlyRecord 如果你打电话 对象.保存。 object.readonly?报告 对象是否是只读的。 将 :readonly => true 传递给任何 finder 方法将标记返回 记录为只读。 :joins 选项现在意味着 :readonly,所以如果 你使用这个选项,保存相同的 记录现在将失败。 使用 find_by_sql 解决方法。
使用find_by_sql
并不是真正的替代方案,因为它返回原始行/列数据,而不是ActiveRecords
。你有两个选择:
-
强制实例变量
@readonly
在记录中为假(hack)
使用:include => :card
代替:join => :card
Rails 2.3.4 及更高版本
2012 年 9 月 10 日之后,上述大部分内容不再适用:
使用Record.find_by_sql
是一个可行的选择
:readonly => true
被自动推断仅如果:joins
被指定没有明确的:select
也不明确的(或finder-scope-inherited ) :readonly
选项(参见active_record/base.rb
中set_readonly_option!
的实现,用于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
值是被忽略——见active_record/associations/has_and_belongs_to_many_association.rb
中的finding_with_ambiguous_select?
。)
总之,除非处理特殊的连接表和has_and_belongs_to_many
,否则@aaronrustad
的答案在Rails 2.3.4 和3.0.0 中适用。
不如果你想获得INNER JOIN
,请不要使用:includes
(:includes
意味着LEFT OUTER JOIN
,它的选择性和效率都低于INNER JOIN
。)李>
【讨论】:
:include 有助于减少完成的查询数,我不知道;但我试图通过将 Tableau/Deckcards 关联更改为 has_many: 来修复它,现在我收到“找不到关联”的消息;我可能不得不为此发布另一个问题 @codeman,是的,:include 将减少查询的数量并且会将包含的表带入您的条件范围(一种没有 Rails 标记记录的隐式连接作为只读的,一旦它在你的 find 中嗅探任何 SQL ish,它就会执行此操作,包括 :join/:select 子句 IIRC 要使 'has_many :a, through => :b' 工作,还必须声明 B 关联,例如'has_many :b; has_many :a, :through => :b',我希望这是你的情况? 这可能在最近的版本中有所改变,但您可以简单地添加 :readonly => false 作为查找方法属性的一部分。 如果您有一个 has_and_belongs_to_many 与指定的自定义 :join_table 关联,则此答案也适用。【参考方案2】:您可以在查找器上指定一个 :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]
【讨论】:
【参考方案3】:这可能在最近的 Rails 版本中有所改变,但解决此问题的适当方法是在查找选项中添加 :readonly => false。
【讨论】:
我不相信是这种情况,至少 2.3.4 是这样 它仍然适用于 Rails 3.0.10,这是我自己的代码中获取具有 :join Fundraiser.donatable.readonly(false) 的范围的示例【参考方案4】:或者在 Rails 3 中,您可以使用 readonly 方法(将“...”替换为您的条件):
( Deck.joins(:card) & Card.where('...') ).readonly(false)
【讨论】:
嗯...我在 Asciicasts 上查找了这两个 Railscast,都没有提到readonly
函数。【参考方案5】:
要停用它...
module DeactivateImplicitReadonly
def custom_join_sql(*args)
result = super
@implicit_readonly = false
result
end
end
ActiveRecord::Relation.send :include, DeactivateImplicitReadonly
【讨论】:
猴子补丁很脆弱 - 很容易被新版本的 Rails 破坏。鉴于还有其他解决方案,绝对不可取。【参考方案6】: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
不能说我理解其中的原理,但至少这是一个快速而干净的解决方法。
【讨论】:
在 Rails 4 中也是一样的。或者你可以做select(quoted_table_name + '.*')
那是辉煌的布朗森。谢谢。
这可能有效,但比使用readonly(false)
更复杂以上是关于是啥导致了这个 ActiveRecord::ReadOnlyRecord 错误?的主要内容,如果未能解决你的问题,请参考以下文章
是啥导致了这个 NullPointerException? [复制]