【问题标题】:Why is using the rails default_scope often recommend against?为什么经常不推荐使用rails default_scope?
【发布时间】:2014-09-25 01:50:21
【问题描述】:

Everywhere on the 互联网人提到使用rails default_scope 是一个坏主意,而在stackoverflow 上default_scope 的热门点击是关于如何覆盖它。这感觉一团糟,值得提出一个明确的问题(我认为)。

那么:为什么不推荐使用 rails default_scope

【问题讨论】:

    标签: ruby-on-rails default-scope


    【解决方案1】:

    问题 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 will affect your model initialization

    在此类模型的新创建实例中,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"
    

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

    有多种方法可以覆盖default_scope 的不同效果。正确地获得complicated 很快,我认为首先不使用default_scope 会是一个更安全的选择。

    【讨论】:

    • 继续往下说:我发现 default_scope 唯一有用的时候是你绝对想在默认情况下急切加载一些关联。 default_scope{eager_load([:category, :cmets])} 。然而!!!如果你在这个模型上做计数查询,比如 Product.count,它会为所有产品进行 eager_load 关联。如果您有 50K 条记录,您的计数查询只需从 15 毫秒到 500 毫秒,因为虽然您想要的只是计数,但您的 default_scope 将离开加入其他所有内容。
    • 在我看来问题出在unscoped 而不是问题#2 中的default_scope
    • @CaptainFogetti 确实如此。我仍然认为将 unscoped 的缺点作为 default_scope 的可能缺点提出是一个好主意。在大多数不平凡的情况下,使用 default_scope 会导致您需要使用 unscoped。这是二级警告(没有更好的术语),在研究方法时很容易错过。
    • 什么时候(几乎)总是想使用它。一个常见的用例是“已删除”或“非活动”记录。
    • 我想default_scope 的一个很好的用途是当你想要对某些东西进行排序时:default_scope { order(:name) }
    【解决方案2】:

    不使用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 的帖子。因此,数据库将引发外键违规,因为它包含引用您要删除的用户的记录。

    【讨论】:

      【解决方案3】:

      default_scope 通常不推荐使用,因为它有时被错误地用于限制结果集。 default_scope 的一个很好的用途是对结果集进行排序。

      我不会在 default_scope 中使用 where,而是为此创建一个范围。

      【讨论】:

      • 即使default_scope 仅包含order,第二个问题“Unscoped 会删除通常可能适用于您的选择的所有范围,包括(但不限于)关联”仍然存在。 unscoped 的这种行为非常出乎意料。
      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

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

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

        所以这将取决于真正的需求。

        【讨论】:

          猜你喜欢
          • 2010-10-03
          • 2012-04-01
          • 2020-05-18
          • 2016-02-23
          • 2017-11-04
          • 2011-10-22
          • 2011-04-11
          • 2021-10-12
          • 2012-12-07
          相关资源
          最近更新 更多