我相信这是Rails 3中的一个错误。我希望有人能指引我朝着正确的方向前进。下面发布的代码纯粹是为了说明这个问题。希望这不会混淆这个问题。
鉴于我有一个Post模型和一个Comment模型。发表has_many评论,评论发表于发帖。
在Post模型上设置default_scope,定义连接()和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
运行以下命令:
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。
答案 0 :(得分:7)
我也遇到了这个问题,但我们确实需要能够在update_all
中使用复杂条件的default_scope
(例如,没有默认范围,切换加载是不可能的,并粘贴一个任何地方的命名范围都没有任何乐趣。我已经在我的修复程序中打开了一个拉取请求:
https://github.com/rails/rails/pull/8449
对于delete_all我已经引发了一个错误,如果有一个连接条件使你更明显你必须做什么(而不是只是抛出连接条件并在所有内容上运行delete_all,你得到一个错误)。
不确定我的拉动请求会对rails人员做些什么,但认为这与此讨论有关。 (另外,如果您需要修复此错误,可以尝试我的分支并在拉取请求上发表评论。)
答案 1 :(得分:4)
如果你有
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!当它有默认范围时。
答案 2 :(得分:1)
您也可以在类级别执行此操作,而无需创建新方法,如下所示:
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
答案 3 :(得分:0)
我认为我不认为它是一个错误。这种行为对我来说似乎合乎逻辑,虽然不是很明显。但是我找到了一个似乎运行良好的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
实际上我正在使用反射来使其更加强大,但上面提到了这个想法。将WHERE逻辑移动到JOIN中可确保它不会应用于不适当的位置。 :readonly
选项是为了抵消Rails默认使用joins
'd个对象的行为。
另外,我知道有些人嘲笑使用default_scope
。但对于多租户应用程序,它非常适合。