【发布时间】:2014-09-25 01:50:21
【问题描述】:
Everywhere on the 互联网人提到使用rails default_scope 是一个坏主意,而在stackoverflow 上default_scope 的热门点击是关于如何覆盖它。这感觉一团糟,值得提出一个明确的问题(我认为)。
那么:为什么不推荐使用 rails default_scope?
【问题讨论】:
标签: ruby-on-rails default-scope
Everywhere on the 互联网人提到使用rails default_scope 是一个坏主意,而在stackoverflow 上default_scope 的热门点击是关于如何覆盖它。这感觉一团糟,值得提出一个明确的问题(我认为)。
那么:为什么不推荐使用 rails default_scope?
【问题讨论】:
标签: ruby-on-rails default-scope
让我们考虑一个基本的例子:
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 will affect your model initialization
在此类模型的新创建实例中,default_scope 将被反映。因此,虽然您可能希望确保不会偶然列出未发布的帖子,但您现在默认创建已发布的帖子。
考虑一个更详细的例子:
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"
=> Unscoped 会删除通常可能适用于您的选择的所有范围,包括(但不限于)关联。
有多种方法可以覆盖default_scope 的不同效果。正确地获得complicated 很快,我认为首先不使用default_scope 会是一个更安全的选择。
【讨论】:
unscoped 而不是问题#2 中的default_scope
default_scope 的一个很好的用途是当你想要对某些东西进行排序时:default_scope { order(:name) }。
不使用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 的帖子。因此,数据库将引发外键违规,因为它包含引用您要删除的用户的记录。
【讨论】:
default_scope 通常不推荐使用,因为它有时被错误地用于限制结果集。 default_scope 的一个很好的用途是对结果集进行排序。
我不会在 default_scope 中使用 where,而是为此创建一个范围。
【讨论】:
default_scope 仅包含order,第二个问题“Unscoped 会删除通常可能适用于您的选择的所有范围,包括(但不限于)关联”仍然存在。 unscoped 的这种行为非常出乎意料。
我只发现default_scope 仅用于在所有情况下将某些参数排序为asc 或desc 顺序。否则我会像瘟疫一样避免它
【讨论】:
对我来说不是一个坏主意,但必须谨慎使用!有一种情况,我总是想在设置字段时隐藏某些记录。
default_scope必须与数据库默认值匹配(例如:{ where(hidden_id: nil) })unscoped 方法可以避免您的default_scope
所以这将取决于真正的需求。
【讨论】: