在 Rails 7 中没有 N+1 查询的 assciation 上取消默认范围

Unscope default scope on assciation without N+1 query in Rails 7

提问人:aldm 提问时间:7/6/2023 最后编辑:aldm 更新时间:7/31/2023 访问量:64

问:

假设我有以下模数:

Categoryhas_many :p roducts has_many :tags has_one :author 具有默认范围ProductTagAuthor

default_scope { where(active: true) }

现在,在显示类别时,我还想显示产品中的所有产品和标签,但我也希望包含来自非活动用户的标签。

最直接的解决方案是删除默认范围并创建显式范围,并在所有位置使用它。但问题是我需要添加很多地方,因此该解决方案不可行。

另一种选择是将新范围添加到Product

def all_tags
  Tag.joins(:author).unscope(author: { where: :active }).where(product_id: id)
end

这可行,但可能导致 N+1 问题 如果每个公司有 10 个产品,每个产品平均有 5 个标签,它将对数据库运行 50 个查询,这不是我想要的。

有什么优雅的方法可以解决 N+1 问题吗?

当然,我可以在控制器中引导所有相关的标签,例如

@tags_per_product = Tag.joins(:author).unscope(author: { where: :active }).where(product_id: product_ids).group_by(&:product_id)

然后在视图中重用,但这看起来有点脏tags_per_product

在 Rails 中,找到简洁优雅的方式总是很重要的,所以我想在这里找到它

提前致谢

Ruby-on-Rails 关联 急切加载 默认范围

评论

0赞 engineersmnky 7/7/2023
为什么不是像这样简单的事情scope :all_tags, -> { tags.unscope(where: :archived)}
0赞 aldm 7/17/2023
谢谢你的回答。我刚刚编辑了这个问题。标记has_one :user 和 user 可以处于活动/非活动状态。我不想显示存档的标签,而是要显示非活动用户的标签
0赞 aldm 7/17/2023
另外,我刚刚尝试了一下,得到了:未定义的局部变量或方法标签,当我在范围内引用时:all_tags
0赞 engineersmnky 7/17/2023
对不起,我印象深刻的是产品被标记了。
0赞 aldm 7/31/2023
谢谢,很抱歉耽搁了。我在新帖子中写了一个解决方案

答:

0赞 aldm 7/31/2023 #1

我是这样解决的

在 tag.rb 中

has_one :author, lambda {
    unscope(where: :active)
}

所以基本上这是可行的,在所有上下文中,我们都会返回被默认范围排除的作者

如果出于某种原因,我们需要包括那些仅在某些上下文中被排除在外的人,我可以通过引入新的

has_one :author_user

或类似的东西