Ruby on rails 默认范围和关联

Ruby on rails 默认范围和关联,ruby-on-rails,default-scope,Ruby On Rails,Default Scope,假设我有一个Post模型和一个Comment模型。使用一种通用模式,Post有很多评论 如果注释设置了默认的\u范围: default_scope where("deleted_at IS NULL") 如何轻松检索帖子上的所有评论,而不考虑范围? 这会产生无效的结果: Post.first.comments.unscoped 这将生成以下查询: SELECT * FROM posts LIMIT 1; SELECT * FROM comments; 而不是: SELECT * FROM

假设我有一个Post模型和一个Comment模型。使用一种通用模式,Post有很多评论

如果注释设置了默认的\u范围:

default_scope where("deleted_at IS NULL")
如何轻松检索帖子上的所有评论,而不考虑范围? 这会产生无效的结果:

Post.first.comments.unscoped
这将生成以下查询:

SELECT * FROM posts LIMIT 1;
SELECT * FROM comments;
而不是:

SELECT * FROM posts LIMIT 1;
SELECT * FROM comments WHERE post_id = 1;
运行:

Post.first.comments
产生:

SELECT * FROM posts LIMIT 1;
SELECT * FROM comments WHERE deleted_at IS NULL AND post_id = 1;
我理解无范围删除所有现有作用域的基本原则,但它不应该意识到并保留关联作用域吗


拉取所有注释的最佳方法是什么?

从Rails 3开始,不推荐使用范围为
的注释。看

class Comment
  def post_comments(post_id)
    with_exclusive_scope { find(all, :conditions => {:post_id => post_id}) }
  end
end

Comment.post_comments(Post.first.id)
前(第2栏):

之后(第3列):


出于一些奇怪的原因

Comment.unscoped { Post.last.comments }
包括
注释
默认范围

但是,

Comment.unscoped { Post.last.comments.to_a }
Comment.unscoped { Post.last.comments.order }
不要包含
注释的
默认范围


我在
rails控制台
rails 3.2.3
的会话中体验到了这一点。这确实是一个非常令人沮丧的问题,违反了最小意外原则

现在,你可以写:

Comment.unscoped.where(post_id: Post.first)
这是IMO最优雅/最简单的解决方案

或:

后者的优点是:

class Comment < ActiveRecord::Base
  # ...

  def self.with_deleted
    scoped.tap { |rel| rel.default_scoped = false }
  end
end
自Rails 4以来,Model.all返回一个ActiveRecord::Relation,而不是一个记录数组。 因此,您可以(也应该)使用
all
而不是
scoped

Post.first.comments.all.tap { |rel| rel.default_scoped = false }

Rails 4.1.1

Comment.unscope(where: :deleted_at) { Post.first.comments }

注意我添加了
.scope
,看起来这个块应该返回某种
ActiveRecord\u AssociationRelation
(什么
.scope
)而不是
ActiveRecord\u Associations\u CollectionProxy
(没有
.scope

这个怎么样

# Use this scope by default
scope :active, -> { where(deleted_at: nil) }

# Use this whenever you want to include all comments regardless of their `deleted_at` value
scope :with_soft_deleted, -> { unscope(where: :deleted_at)

default_scope, -> { active }
post.comments
将触发此查询:

SELECT "comments".* FROM "comments" WHERE "comments"."deleted_at" IS NULL AND "comments"."post_id" = $1;
post.comments.with_soft_deleted
将发送以下内容:

SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = $1;

为什么不考虑一下,创建一个
作用域:active,其中(“deleted_at IS NULL”)
并在需要时调用它呢?我也会这么做。如果你发现自己不得不“撤销”你的违约,那么这不是一个好的违约。我不同意这一点。我认为在默认情况下隐藏并在需要时显式重写该默认值是一种很好的做法。这在某种程度上与Rails的XSS对策有关。您必须在Rails 2中转义每个用户生成的内容输出。现在,默认情况下所有内容都被转义,即使有很多非用户生成的内容,您也必须手动覆盖它。这是关于安全的。在这种情况下的信息安全。你可能会因为在你的页面上有非法评论而被起诉,因为你忘记了命名的范围。或者客户会在某个页面上看到未发布的内容,这让我觉得有点不舒服。帖子/评论,只是一个例子,你不会想在许多不同的模型上重复这一点吧?我意识到我最初也没有提到这一点。这会很快变得很难看。如果这对你的口味来说太粗糙了(真的很粗糙),首先尝试使用默认的范围重新评估。哇!这是铁轨上的预裂!这样,默认的_范围就不可用了。应该有post.unscoped_评论和comment.unscoped_post方法生成…我同意。如果能有post.unscoped_的评论就太好了。但至少您可以始终执行Comment.unscoped{post.comments}暂时禁用默认作用域(对于unscoped{}块中的所有内容)…我遇到了您的问题,在我的例子中,
post.last.comments.order
起作用,而
post.last.comments.to_a
被破坏。你找到这方面的线索了吗?我将尝试将我的应用程序升级到最新的Rails,看看这是否已修复。3.2.6中也会出现这种情况,可能是一个bug。在
Rails 4.1.1
中仍然存在,
order(:id)
有助于排除默认的顺序。有关此问题的更多信息,请参阅。
Comment.unscope(where: :deleted_at) { Post.first.comments }
Comment.unscoped { Post.first.comments.scope }
# Use this scope by default
scope :active, -> { where(deleted_at: nil) }

# Use this whenever you want to include all comments regardless of their `deleted_at` value
scope :with_soft_deleted, -> { unscope(where: :deleted_at)

default_scope, -> { active }
SELECT "comments".* FROM "comments" WHERE "comments"."deleted_at" IS NULL AND "comments"."post_id" = $1;
SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = $1;