Rails 6:如何同时创建has_one多态关联的两条记录
解决Rails多态关联中创建关联对象时反向关联为nil的问题
当前代码的核心问题是:Person创建Hat时,Hat的after_create回调触发时机早于Person关联字段的更新,导致self.person为nil报错。以下是两种可行的解决方法:
方法一:改用after_commit回调确保关联完成
after_create会在Hat写入数据库后立即执行,此时Person的wearable_type和wearable_id尚未更新,Hat无法找到对应的Person。改用after_commit并限定仅在创建时触发,会在整个事务(包括Person的关联更新)提交后执行,此时关联已完全建立。
修改后的代码:
class Person < ApplicationRecord belongs_to :wearable, polymorphic: true, required: false, dependent: :destroy after_create do # 用Rails内置的create_wearable方法自动创建Hat并完成关联 create_wearable!(type: 'Hat') if wearable.nil? end end class Hat < ApplicationRecord has_one :person, as: :wearable, class_name: 'Person' # 替换after_create为after_commit,确保事务提交后执行 after_commit :embroider_initials, on: :create private def embroider_initials embroider(self.person.initials) end end
方法二:创建Hat时直接传递所需属性
如果不想调整回调时机,可以在Person创建Hat时直接传入所需的Person属性,避免依赖关联查询。这种方式需要给Hat添加临时属性或数据库字段:
class Person < ApplicationRecord belongs_to :wearable, polymorphic: true, required: false, dependent: :destroy after_create do if wearable.nil? # 创建Hat时直接传入Person的initials wearable = Hat.create(initials: self.initials) self.wearable = wearable save end end end class Hat < ApplicationRecord has_one :person, as: :wearable, class_name: 'Person' # 添加临时属性或数据库字段存储initials attr_accessor :initials after_create do embroider(self.initials) end end
推荐方案:优先选择方法一,无需修改模型属性结构,完全利用Rails关联机制保证数据一致性,逻辑更清晰。
内容的提问来源于stack exchange,提问作者emersonthis
相关产品推荐
相关产品推荐

