是啥导致了这个 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_idhand_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 =&gt; :card 代替:join =&gt; :card

Rails 2.3.4 及更高版本

2012 年 9 月 10 日之后,上述大部分内容不再适用:

使用Record.find_by_sql 一个可行的选择 :readonly =&gt; true 被自动推断如果:joins被指定没有明确的:select也不明确的(或finder-scope-in​​herited ) :readonly 选项(参见active_record/base.rbset_readonly_option! 的实现,用于Rails 2.3.4,或to_aactive_record/relation.rbcustom_join_sqlactive_record/relation/query_methods.rb 的Rails 3.0.0 的实现) 然而,:readonly =&gt; 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? [复制]

Python MySQLdb错误 - 是啥导致了这个

是啥导致了这个“jpackage 不存在”错误?

是啥导致了这个 301 重定向? [关闭]

是啥导致了这个 ActiveRecord::ReadOnlyRecord 错误?

Spring Boot:是啥导致了这个 TransientDataAccessResourceException?