default_scope在某些情况下会中断(更新|删除|销毁)_all

我相信这是Rails 3中的一个错误。我希望有人能指引我朝着正确的方向前进。 下面发布的代码纯粹是为了说明这个问题。 希望这不会混淆问题。

鉴于我有一个Post模型和一个Comment模型。 发表has_many评论,评论发表于发帖。

在Post模型上设置default_scope,定义join()和where()关系。 在这种情况下,()依赖于连接()。

通常post不依赖于评论。 再说一遍,我只想举一个简单的例子。 当where()依赖于join()时,这可能是任何情况。

class Post  :destroy default_scope joins(:comments).where("comments.id < 999") end class Comment  true end 

运行以下命令:

 Post.update_all(:title => Time.now) 

生成以下查询,并最终抛出ActiveRecord :: StatementInvalid:

 UPDATE `posts` SET `title` = '2010-10-15 15:59:27' WHERE (comments.id < 999) 

同样,update_all,delete_all,destroy_all的行为方式相同。 当我的应用程序在尝试更新counter_cache时抱怨时,我发现了这种行为。 最终深入研究update_all。

我也遇到了这个问题,但是我们确实需要能够在default_scope使用具有复杂条件的update_all (例如,没有默认范围的eager-load是不可能的,并且在任何地方粘贴命名范围都没有任何乐趣)。 我已经在我的修复程序中打开了一个拉取请求:

https://github.com/rails/rails/pull/8449

对于delete_all我已经引发了一个错误,如果有一个连接条件让你更明显你要做什么(而不是只是抛出连接条件并在所有东西上运行delete_all,你得到一个错误)。

不知道有什么轨道人员会对我的拉动请求做些什么,但认为这与这个讨论有关。 (另外,如果您需要修复此错误,可以尝试我的分支并在拉取请求上发表评论。)

我也碰到了这个 。

如果你有

 class Topic < ActiveRecord::Base default_scope :conditions => "forums.preferences > 1", :include => [:forum] end 

你做了

 Topic.update_all(...) 

它会失败的

 Mysql::Error: Unknown column 'forums.preferences' in 'where clause' 

解决这个问题的方法是:

 Topic.send(:with_exclusive_scope) { Topic.update_all(...) } 

您可以使用此代码对其进行修补(并在environment.rb或其他地方需要它)

 module ActiveRecordMixins class ActiveRecord::Base def self.update_all!(*args) self.send(:with_exclusive_scope) { self.update_all(*args) } end def self.delete_all!(*args) self.send(:with_exclusive_scope) { self.delete_all(*args) } end end end 

结束

然后就是你update_all! 或者delete_all! 当它有默认范围时。

您也可以在类级别执行此操作,而无需创建新方法,如下所示:

 def self.update_all(*args) self.send(:with_exclusive_scope) { super(*args) } end def self.delete_all(*args) self.send(:with_exclusive_scope) { super(*args) } end 

我认为我不认为它是一个错误。 这种行为对我来说似乎合乎逻辑,虽然不是很明显。 但是我找到了一个似乎运行良好的SQL解决方案。 使用您的示例,它将是:

 class Post < ActiveRecord::Base has_many :comments, :dependent => :destroy default_scope do with_scope :find => {:readonly => false} do joins("INNER JOIN comments ON comments.post_id = posts.id AND comments.id < 999") end end end 

实际上,我正在使用reflection来使其更加健壮,但上面的内容让人反思。 将WHERE逻辑移动到JOIN中可确保它不会应用于不适当的位置。 :readonly选项是抵消Rails默认的joins对象的默认行为。

另外,我知道有些人嘲笑使用default_scope 。 但对于多租户应用程序,它是一个完美的选择。