求助:Ruby on Rails STI模型动态生成作用域问题
解决Ruby on Rails STI模型动态生成作用域的问题
嘿,我懂你遇到的这个头疼问题——STI模型里动态生成的作用域没生效对吧?其实根源就在Rails的模型加载顺序上,尤其是开发环境的懒加载机制在搞鬼。
问题分析
你在Unit类里直接调用types.each生成作用域,但当Unit类被加载时,它的子类(Academy、Faculty这些)还没被Rails加载呢!这时候descendants返回的是空数组,循环根本不会执行,自然就没生成对应的作用域。
几种靠谱的解决方案
方案1:用inherited回调动态添加作用域(推荐)
这是最健壮的方法,每当有子类继承Unit时,自动为父类添加对应的作用域,完全不受加载顺序影响:
# app/models/unit.rb class Unit < ApplicationRecord def self.inherited(subclass) super # 生成对应作用域,比如Academy对应academies作用域 scope subclass.name.tableize.to_sym, -> { where(type: subclass.name) } end end
不管是开发还是生产环境,只要有子类继承Unit,作用域就会自动创建,完美规避懒加载的坑。
方案2:强制加载所有子类再生成作用域
如果你更倾向保留原来的types方法思路,可以修改types方法,先手动加载所有Unit的子类,再获取descendants:
# app/models/unit.rb class Unit < ApplicationRecord def self.types # 先加载app/models/units目录下的所有子类文件 Dir[Rails.root.join('app/models/units/*.rb')].each { |file| require_dependency file } descendants.map(&:name) end # 现在types里已经包含子类,循环能正常生成作用域 types.each do |type| scope type.tableize.to_sym, -> { where(type: type) } end end
注意要确保子类都放在app/models/units目录下,后续子类路径变化时记得同步修改。
方案3:调整开发环境预加载配置(不推荐)
你提到了config/environments/development.rb里的config.eager_load,如果把它设为true,Rails启动时会预加载所有模型,原来的代码也能工作。但开发环境开启预加载会让启动变慢,修改模型后还要重启服务器,开发体验大打折扣,所以不太推荐这种方式。
验证一下
修改完代码后重启Rails控制台,试试调用Unit.academies或者Unit.faculties,应该就能正确筛选出对应类型的记录啦!
内容的提问来源于stack exchange,提问作者msdundar
相关产品推荐
相关产品推荐

