Why is using the rails default_scope often recommend against?

Everywhere on the internet people mention that using the rails default_scope is a bad idea, and the top hits for default_scope on stackoverflow are about how to overwrite it. This feels messed up, and merits an explicit question (I think).

So: why is using the rails default_scope recommended against?

41492 次浏览

问题1

让我们考虑一个基本的例子:

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

使用默认 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的效果。经过一个快速的谷歌,你可能会发现关于 unscoped。看看接下来会发生什么:

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

未作用域移除通常应用于您的选择的所有作用域,包括(但不限于)关联。

有多种方法可以覆盖 default_scope的不同效果。正确的得到 很复杂非常快,我认为不使用 default_scope在第一位,将是一个更安全的选择。

通常建议不要使用 default _ scope,因为它有时不正确地用于限制结果集。Default _ scope 的一个很好的用法是对结果集进行排序。

我不会在 default _ scope 中使用 where,而是为其创建一个作用域。

我只发现 default_scope只有在排序一些参数在 ascdesc顺序在所有情况下有用。否则我会像躲瘟疫一样躲着它

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

例如:

    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的帖子。因此,数据库将抛出外键冲突,因为它包含引用要删除的用户的记录。

对我来说是 没有一个 坏主意,但必须谨慎使用!.有一种情况,我总是希望在设置字段时隐藏某些记录。

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

因此,这将取决于和真正的需要。