9

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

鉴于我有一个 Post 模型和一个 Comment 模型。Post has_many Comments 和 Comment belongs_to Post。

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

通常帖子不会依赖于评论。同样,我只想举一个简单的例子。当 where() 依赖于 joins() 时,这可能是任何情况。

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。

4

4 回答 4

7

我也遇到了这个问题,但我们确实需要能够在update_all复杂的条件下使用default_scope(例如,没有默认作用域急切加载是不可能的,并且在任何地方都粘贴命名作用域一点也不好玩)。我在这里用我的修复打开了一个拉取请求:

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

对于 delete_all,如果存在连接条件以使您必须做的事情更加明显,我会引发错误(而不是仅仅抛出连接条件并在所有内容上运行 delete_all,您会收到错误)。

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

于 2012-12-07T13:27:40.360 回答
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!或删除全部!当它具有默认范围时。

于 2011-02-05T00:03:21.993 回答
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
于 2012-08-28T18:58:29.893 回答
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. 但对于多租户应用程序,它非常适合。

于 2013-03-21T17:27:45.820 回答