Rails 4(Mongoid):从嵌入式文档after_initialize回调获取关联文档_id
问题分析与解决方案
这个问题的核心在于Mongoid嵌入式文档的生命周期——after_initialize回调触发的时机太早了,这时候你的Asset实例还没和Store建立关联,所以self.store才会返回nil。
为什么会出现nil?
当你创建Asset实例时(不管是Asset.new还是store.assets.build),after_initialize会在对象刚初始化完成后立刻执行:
- 如果是直接调用
Asset.new,实例根本没绑定任何Store,自然拿不到关联对象; - 如果是用
store.assets.build,Mongoid在初始化Asset时还没把父Store的引用赋值给子对象的store属性,所以此时self.store依然是nil。
只有当Asset被正式嵌入到Store的关联集合中后,父对象的引用才会被赋值给子对象。
解决方案
方案1:用父模型的after_add回调(推荐)
在Store模型中添加after_add回调,这个回调会在Asset被成功添加到assets集合时触发,此时Asset已经和Store建立了完整关联,就能正常获取store.id了:
class Store include Mongoid::Document embeds_many :assets after_add :trigger_asset_store_id_logic def trigger_asset_store_id_logic(asset) asset.get_store_id end end class Asset include Mongoid::Document embedded_in :store def get_store_id logger.info self.store.id # 这里能正常输出Store的_id end end
方案2:创建时显式传递父对象引用
如果是手动创建Asset,可以直接把Store实例传入初始化参数,这样after_initialize里就能拿到关联:
existing_store = Store.find(params[:store_id]) asset = Asset.new(store: existing_store) # 此时after_initialize触发时,self.store已经有值了
补充:加载已存在的Asset
如果是从数据库中加载已经存在的Asset(比如store.assets.first),after_initialize里是能正常拿到self.store的,因为加载时Mongoid会自动把父对象的引用赋值给子对象。
内容的提问来源于stack exchange,提问作者Bnaya Zil
相关产品推荐
相关产品推荐

