default_scope 和关联

发布于 2024-09-27 15:45:44 字数 729 浏览 4 评论 0原文

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

如果评论设置了 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;

我理解无作用域删除所有现有作用域的基本原理,但它不应该意识到并保持关联作用域吗?

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

Suppose I have a Post model, and a Comment model. Using a common pattern, Post has_many Comments.

If Comment has a default_scope set:

default_scope where("deleted_at IS NULL")

How do I easily retrieve ALL comments on a post, regardless of scope?
This produces invalid results:

Post.first.comments.unscoped

Which generates the following queries:

SELECT * FROM posts LIMIT 1;
SELECT * FROM comments;

Instead of:

SELECT * FROM posts LIMIT 1;
SELECT * FROM comments WHERE post_id = 1;

Running:

Post.first.comments

Produces:

SELECT * FROM posts LIMIT 1;
SELECT * FROM comments WHERE deleted_at IS NULL AND post_id = 1;

I understand the basic principle of unscoped removing all existing scopes, but shouldn't it be aware and to keep the association scope?

What is the best way to pull ALL comments?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

瞎闹 2024-10-04 15:45:44

由于一些奇怪的原因,

Comment.unscoped { Post.last.comments }

包含 Commentdefault_scope

但是,

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

包含 default_scope评论

我在使用 Rails 3.2.3rails console 会话中经历了这一点。

For some strange reasons,

Comment.unscoped { Post.last.comments }

includes the default_scope of Comment,

however,

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

do not include the default_scope of Comment.

I experienced this in a rails console session with Rails 3.2.3.

伊面 2024-10-04 15:45:44

with_exlusive_scope 从 Rails 3 开始已弃用。请参阅此提交

之前 (Rails 2):

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

之后 (Rails 3):

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

with_exlusive_scope is deprecated as of Rails 3. See this commit.

Before (Rails 2):

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

After (Rails 3):

Comment.unscoped { Post.find(post_id).comments }
找回味觉 2024-10-04 15:45:44

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

Rails 4.1.1

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

Or

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

Note that I added .scope, it seems like this block should return kind of ActiveRecord_AssociationRelation (what .scope does) not ActiveRecord_Associations_CollectionProxy (without a .scope)

我爱人 2024-10-04 15:45:44

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

现在,您可以只写:

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

这是我认为最优雅/最简单的解决方案。

或者:

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 }

This is indeed a very frustrating problem which violates the principle of least surprise.

For now, you can just write:

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

This is the most elegant/simple solution IMO.

Or:

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

The advantage of the latter:

class Comment < ActiveRecord::Base
  # ...

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

Then you can make fun things:

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

Since Rails 4, Model.all returns an ActiveRecord::Relation , rather than an array of records.
So you can (and should) use all instead of scoped:

Post.first.comments.all.tap { |rel| rel.default_scoped = false }
时间你老了 2024-10-04 15:45:44

这个怎么样?

# 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;

How about this?

# 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 would fire this query:

SELECT "comments".* FROM "comments" WHERE "comments"."deleted_at" IS NULL AND "comments"."post_id" = $1;

post.comments.with_soft_deleted would send this:

SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = $1;
鹿童谣 2024-10-04 15:45:44
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)
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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文