default_scope和association

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

如果Comment设置了default_scope:

default_scope where("deleted_at IS NULL") 

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

 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; 

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

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

出于某些奇怪的原因,

 Comment.unscoped { Post.last.comments } 

包括default_scope

然而,

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

包括Commentdefault_scope

我在使用Rails 3.2.3rails console会话中体验过这一点。

从Rails 3 with_exlusive_scope ,不推荐使用with_exlusive_scope 。请参阅此提交 。

之前(Rails 2):

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

之后(Rails 3):

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

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

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

现在,你可以写:

 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 } 
 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)