default_scope和association

rel*_*eod 25 ruby-on-rails default-scope

假设我有一个Post模型和一个Comment模型.使用常见模式,Post has_many Comments.

如果Comment设置了default_scope:

default_scope where("deleted_at IS NULL")
Run Code Online (Sandbox Code Playgroud)

无论范围如何,如何轻松检索帖子上的所有评论?这会产生无效结果:

Post.first.comments.unscoped
Run Code Online (Sandbox Code Playgroud)

这会生成以下查询:

SELECT * FROM posts LIMIT 1;
SELECT * FROM comments;
Run Code Online (Sandbox Code Playgroud)

代替:

SELECT * FROM posts LIMIT 1;
SELECT * FROM comments WHERE post_id = 1;
Run Code Online (Sandbox Code Playgroud)

运行:

Post.first.comments
Run Code Online (Sandbox Code Playgroud)

生产:

SELECT * FROM posts LIMIT 1;
SELECT * FROM comments WHERE deleted_at IS NULL AND post_id = 1;
Run Code Online (Sandbox Code Playgroud)

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

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

Vik*_*ary 16

出于某些奇怪的原因,

Comment.unscoped { Post.last.comments }
Run Code Online (Sandbox Code Playgroud)

包括default_scopeComment,

然而,

Comment.unscoped { Post.last.comments.to_a }
Comment.unscoped { Post.last.comments.order }
Run Code Online (Sandbox Code Playgroud)

不要包括default_scopeComment.

我在一次rails console会议中经历了这个Rails 3.2.3.


cri*_*spy 9

with_exlusive_scope自Rails 3开始不推荐使用.请参阅此提交.

之前(Rails 2):

Comment.with_exclusive_scope { Post.find(post_id).comments }
Run Code Online (Sandbox Code Playgroud)

之后(Rails 3):

Comment.unscoped { Post.find(post_id).comments }
Run Code Online (Sandbox Code Playgroud)


fre*_*oid 8

Rails 4.1.1

Comment.unscope(where: :deleted_at) { Post.first.comments }
Run Code Online (Sandbox Code Playgroud)

要么

Comment.unscoped { Post.first.comments.scope }
Run Code Online (Sandbox Code Playgroud)

注意,我补充说.scope,似乎这个块应该返回那种ActiveRecord_AssociationRelation(什么.scope不)ActiveRecord_Associations_CollectionProxy (没有a .scope)


Dam*_*ien 6

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

现在,你可以写:

Comment.unscoped.where(post_id: Post.first)
Run Code Online (Sandbox Code Playgroud)

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

要么:

Post.first.comments.scoped.tap { |rel| rel.default_scoped = false }
Run Code Online (Sandbox Code Playgroud)

后者的优点:

class Comment < ActiveRecord::Base
  # ...

  def self.with_deleted
    scoped.tap { |rel| rel.default_scoped = false }
  end
end
Run Code Online (Sandbox Code Playgroud)

然后你可以取笑:

Post.first.comments.with_deleted.order('created_at DESC')
Run Code Online (Sandbox Code Playgroud)

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

Post.first.comments.all.tap { |rel| rel.default_scoped = false }
Run Code Online (Sandbox Code Playgroud)


Fai*_*sal 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)
Run Code Online (Sandbox Code Playgroud)

  • 哇!这在轨道上已经很糟糕了!这样,default_scope就无法使用了。应该生成 post.unscoped_comments 和 comment.unscoped_post 方法... (2认同)