为什么经常建议使用rails default_scope?


125

无处不 互联网人提到,使用轨道default_scope是一个坏主意,而对于排名靠前default_scope的计算器是关于如何将其覆盖。这感觉很混乱,值得提出一个明确的问题(我认为)。

那么:为什么default_scope建议使用导轨?

Answers:


192

问题1

让我们考虑基本示例:

class Post < ActiveRecord::Base
  default_scope { where(published: true) }
end

设置为default的动机published: true可能是确保要显示未发布的(私人)帖子时必须让您明确。到目前为止,一切都很好。

2.1.1 :001 > Post.all
  Post Load (0.2ms)  SELECT "posts".* FROM "posts"  WHERE "posts"."published" = 't'

好吧,这几乎是我们期望的。现在让我们尝试:

2.1.1 :004 > Post.new
 => #<Post id: nil, title: nil, published: true, created_at: nil, updated_at: nil>

这是默认范围的第一个大问题:

=> default_scope将影响您的模型初始化

在这种模型的新创建实例中,default_scope将反映出来。因此,尽管您可能希望确保不要偶然列出未发布的帖子,但现在默认情况下正在创建已发布的帖子。

问题2

考虑一个更详细的示例:

class Post < ActiveRecord::Base
  default_scope { where(published: true) }
  belongs_to :user
end 

class User < ActiveRecord::Base
  has_many :posts
end

让我们获得第一个用户的帖子:

2.1.1 :001 > User.first.posts
  Post Load (0.3ms)  SELECT "posts".* FROM "posts"  WHERE "posts"."published" = 't' AND "posts"."user_id" = ?  [["user_id", 1]]

这看起来像预期的一样(请确保一直滚动到右侧,以查看有关user_id的部分)。

现在,我们要获取所有帖子的列表-包括未发布的-针对已登录用户的视图说。您将意识到必须“覆盖”或“撤消”的影响default_scope。快速浏览Google之后,您可能会发现有关的信息unscoped。看看接下来会发生什么:

2.1.1 :002 > User.first.posts.unscoped
  Post Load (0.2ms)  SELECT "posts".* FROM "posts"

=> Unscoped删除通常可能适用于您选择的所有范围,包括(但不限于)关联。

有多种方法可以覆盖的不同效果default_scope。实现正确的选择很快就会变得复杂,我认为一开始不使用default_scope,将是一个更安全的选择。


2
总结一下:我唯一发现default_scope有用的时间是当您绝对想默认加载一些关联时。default_scope {eager_load([:category,:comments])}。然而!!!如果您要在此模型(例如Product.count)上进行计数查询,它将为所有产品建立eager_load关联。而且,如果您有5万条记录,那么您的计数查询就从15ms变为了500ms,因为尽管您只想计数,但default_scope会保留所有其他内容。
konung 2015年

16
在我看来,问题在于unscoped而不是default_scope问题2
船长Fogetti

4
@CaptainFogetti确实。我仍然认为将unscoped的缺点介绍为default_scope的可能缺点是一个好主意。在大多数情况下,使用default_scope会导致您需要使用无作用域。这是第二级警告(缺少更好的术语),在研究方法时很容易错过。
wrtsprt

1
您的答案中的用例存在的问题是,在许多情况下您想查找未发布的帖子。实际上,我认为找到公开的帖子是一个特例。您唯一要发表的帖子是某人正在查看公共页面时。但是很多时候您想查看未发布的帖子。
B

3
default_scope想当您希望对某些内容进行排序时,是一个很好的选择:default_scope { order(:name) }
user2985898

9

不使用的另一个原因default_scope是,当您删除与该模型具有一对多关系的default_scope模型实例时

考虑例如:

    class User < ActiveRecord::Base
      has_many :posts, dependent: :destroy
    end 

    class Post < ActiveRecord::Base
      default_scope { where(published: true) }
      belongs_to :user
    end

呼叫user.destroy将删除所有的帖子published,但不会删除所有的帖子unpublished。因此,数据库将抛出外键冲突,因为它包含引用您要删除的用户的记录。


6

通常建议不要使用default_scope,因为有时会错误地使用它来限制结果集。default_scope的一个很好的用途是对结果集进行排序。

我会避免where在default_scope中使用它,而是为此创建一个作用域。


1
即使default_scope仅包含,第二个问题“不受范围的删除通常可能适用于您的选择的所有范围,包括(但不限于)关联”仍然存在order。的这种行为unscoped是非常意外的。
Zack Xu

1

对我来说这不是一个坏主意,但必须谨慎使用!在某些情况下,设置字段时我总是想隐藏某些记录。

  1. 优选地,default_scope必须与DB默认值相匹配(例如:{ where(hidden_id: nil) }
  2. 当您完全确定要显示这些记录时,总有unscoped一种方法可以避免default_scope

因此,这取决于实际需求。


0

我发现default_scope仅在将某些参数排序ascdesc在所有情况下排序时才有用。否则我会像瘟疫一样避免

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.