Rails 7中如何预加载STI模型的关联关系?
Rails 7中如何预加载STI模型的关联关系?
嘿,这个STI场景下的关联预加载问题确实有点坑!我之前处理类似需求的时候也踩过同款N+1的坑,给你分享两种靠谱的解决思路:
方法一:用ActiveRecord预加载器批量加载子类关联
你之前尝试用预加载器但没成功,大概率是用错了类名或者调用方式。Rails 7里正确的预加载器是ActiveRecord::Preloader,不是ActiveRecord::Associations::Preloader。按照这个步骤来:
- 先获取用户的所有缺勤记录:
absences = Absence.where(user: xxx)
- 从集合里筛选出年假类型的记录(用
grep比select更简洁):
vacations = absences.grep(Vacation)
- 用预加载器一次性加载所有年假对应的配额关联:
ActiveRecord::Preloader.new.preload(vacations, :vacation_contingent)
这样之后,你遍历vacations访问vacation_contingent时,只会触发1次额外的数据库查询,完美解决N+1问题~
方法二:链式写法一步到位
如果你想让代码更紧凑,可以用tap把筛选和预加载整合到一次调用里:
absences = Absence.where(user: xxx).tap do |records| vacations = records.grep(Vacation) ActiveRecord::Preloader.new.preload(vacations, :vacation_contingent) end
这种方式和方法一本质相同,依然只触发两次数据库查询:一次查所有缺勤记录,一次查所有需要的年假配额,同时还能保持代码的可读性。
为什么你之前的预加载器调用没生效?
你之前用的ActiveRecord::Associations::Preloader在Rails 7里已经被整合到ActiveRecord::Preloader中,属于过时用法;而且正确的调用逻辑是直接通过preload方法加载,不需要手动初始化后再调用call。换成ActiveRecord::Preloader就能正常工作啦。
另外你提到的分开查询Vacation和其他缺勤的方式确实不够优雅,还会多一次数据库请求,用预加载器的方式既保留了“先获取所有缺勤再处理”的业务逻辑,又能高效控制查询次数。
备注:内容来源于stack exchange,提问作者SomeDude
相关产品推荐
相关产品推荐

