26

假设我有一个 Post 模型和一个 Comment 模型。使用通用模式,发布 has_many Comments。

如果 Comment 设置了 default_scope:

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;

我理解 unscoped 删除所有现有范围的基本原则,但不应该知道并保持关联范围吗?

提取所有评论的最佳方式是什么?

4

6 回答 6

17

由于一些奇怪的原因,

Comment.unscoped { Post.last.comments }

包括, default_scope_Comment

然而,

Comment.unscoped { Post.last.comments.to_a }
Comment.unscoped { Post.last.comments.order }

不包括of 。_default_scopeComment

我在rails consoleRails 3.2.3.

于 2012-05-09T09:40:42.957 回答
9

with_exlusive_scope自 Rails 3 起已弃用。请参阅此提交

之前(轨道 2):

Comment.with_exclusive_scope { Post.find(post_id).comments }

之后(轨道 3):

Comment.unscoped { Post.find(post_id).comments }
于 2011-01-21T13:22:20.563 回答
8

Rails 4.1.1

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

或者

Comment.unscoped { Post.first.comments.scope }

请注意,我添加了.scope,似乎这个块应该返回一种ActiveRecord_AssociationRelation(什么.scope)而不是ActiveRecord_Associations_CollectionProxy (没有.scope

于 2014-05-27T10:15:07.213 回答
6

这确实是一个非常令人沮丧的问题,它违反了最小意外原则。

现在,你可以写:

Comment.unscoped.where(post_id: Post.first)

这是 IMO 最优雅/最简单的解决方案。

或者:

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

后者的优势:

class Comment < ActiveRecord::Base
  # ...

  def self.with_deleted
    scoped.tap { |rel| rel.default_scoped = false }
  end
end

然后你可以做一些有趣的事情:

Post.first.comments.with_deleted.order('created_at DESC')

从 Rails 4 开始, Model.all 返回一个 ActiveRecord::Relation ,而不是一个记录数组。所以你可以(并且应该)使用all而不是scoped

Post.first.comments.all.tap { |rel| rel.default_scoped = false }
于 2013-05-17T18:21:01.053 回答
1

这个怎么样?

# 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;
于 2020-02-03T08:27:30.593 回答
0
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)
于 2010-10-19T06:06:19.373 回答