2010-10-15 93 views
9

我相信这是Rails 3中的一个bug。我希望这里有人能够引导我朝着正确的方向前进。下面的代码,纯粹是为了说明这个问题。希望这不会混淆这个问题。default_scope break(update | delete | destroy)_all在某些情况下

鉴于我有一个Post模型和一个评论模型。发布has_many评论和评论belongs_to发布。

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

通常帖子不会依赖于评论。再次,我只想举一个简单的例子。这可能是where()依赖于连接()的任何情况。

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

I ran into this as well

如果你有

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!当它有一个默认范围。

+0

嘿,本,你对此有疑问? – 2012-07-10 20:07:29

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 
7

我有这个问题也有,但是我们真正需要的是能够使用update_all在复杂的条件(例如,如果没有默认范围,则不可能进行急切加载,并且从字面上粘贴命名范围无处不在)。我在这里开了一个拉请求与我的解决办法:

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

对于DELETE_ALL我提出了一个错误,如果有一个连接条件,使之更加明显,你需要做的(而不是仅仅扔加入什么条件和运行delete_all的一切,你会得到一个错误)。

不确定铁路员工会如何处理我的拉线请求,但认为它与此讨论有关。 (另外,如果你需要修正这个错误,你可以试试我的分支并发布对请求的评论。)

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的行为。

另外,我知道有些人嘲笑使用default_scope。但对于多租户应用程序来说,这非常合适。

相关问题