You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails 7测试预加载数据过滤时的Bullet查询优化报错问题

Rails 7中Bullet针对可选关联预加载的测试报错解决方法

问题场景

在Rails 7的index动作中使用posts.includes([:user, :status])预加载数据,Post与User为可选关联(存在无User的Post)。基于不同参数过滤数据时,多数场景正常,但使用scope :no_user, -> { where(user_id: nil) }过滤无User的Post时,浏览器无报错,但Minitest控制器测试触发Bullet报错:

Bullet::Notification::UnoptimizedQueryError
AVOID eager loading detected
  Post => [:user]
  Remove from your query: .includes([:user])

若移除includes中的:user,其他过滤测试则触发相反报错:

USE eager loading detected
  Post => [:user]
  Add to your query: .includes([:user])

尝试left_outer_joins(:user)也会出现相同报错,希望无需针对该过滤编写条件语句消除测试报错。

可行解决方案

1. 给Bullet添加白名单(推荐)

修改config/initializers/bullet.rb,添加针对Post模型:user关联的白名单配置:

Bullet.add_whitelist type: :unused_eager_loading, class_name: "Post", association: :user

这个配置会告诉Bullet:当Post的:user预加载未被使用时,不要抛出UnoptimizedQueryError。刚好匹配no_user scope的场景——此时所有查询结果都没有User,预加载的:user确实不会被用到,但其他场景下预加载依然有效,完全不用修改业务代码。

2. 动态切换预加载方式(备选)

如果可以接受极少量的条件判断,可利用Rails的preload和includes特性调整预加载逻辑:

posts = Post.all.preload(:status)
posts = posts.includes(:user) unless params[:filter] == 'no_user'
posts = posts.no_user if params[:filter] == 'no_user'

但此方法需要针对过滤参数做判断,不符合“无需编写条件语句”的需求,仅作备选。

3. 全局关闭未使用预加载检测(不推荐)

若项目中存在大量类似可选关联场景,可在bullet.rb中关闭全局的未使用预加载检测:

Bullet.unused_eager_loading_enable = false

但该方法会失去Bullet对其他不必要预加载的检测能力,不利于项目性能优化,谨慎使用。

内容的提问来源于stack exchange,提问作者Anton Zaharia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 16:22:26