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

rel*_*eod 9 ruby-on-rails default-scope destroy

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

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

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

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

class Post < ActiveRecord::Base
  has_many :comments, :dependent => :destroy

  default_scope joins(:comments).where("comments.id < 999")
end

class Comment < ActiveRecord::Base
  belongs_to :post, :counter_cache => true
end
Run Code Online (Sandbox Code Playgroud)

运行以下命令:

Post.update_all(:title => Time.now)
Run Code Online (Sandbox Code Playgroud)

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

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

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

小智 7

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

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

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

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


Ben*_*ley 5

也遇到了这个

\n\n

如果你有

\n\n
class Topic < ActiveRecord::Base\n  default_scope :conditions => "forums.preferences > 1", :include => [:forum]\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n

你做了一个

\n\n
Topic.update_all(...)\n
Run Code Online (Sandbox Code Playgroud)\n\n

it\xe2\x80\x99 将失败

\n\n
Mysql::Error: Unknown column \'forums.preferences\' in \'where clause\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

解决这个问题的方法是:

\n\n
Topic.send(:with_exclusive_scope) { Topic.update_all(...) }\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
module ActiveRecordMixins\n  class ActiveRecord::Base\n    def self.update_all!(*args)\n      self.send(:with_exclusive_scope) { self.update_all(*args) }\n    end\n    def self.delete_all!(*args)\n      self.send(:with_exclusive_scope) { self.delete_all(*args) }\n    end\n  end\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n

结尾

\n\n

然后你就 update_all 了!或删除全部!当它有默认范围时。

\n