Rails delegated types场景下委托对象验证失败的正确处理方法是什么?
Rails delegated_type 关联对象验证失败的正确处理方案
问题根因
你遇到的数据库非空报错本质是:delegated_type本质是多态关联,Rails默认不会自动校验关联的inboxable对象,也不会自动关联保存逻辑。当Mailbox验证失败无法持久化时,Inbox插入时缺少inboxable_id就会触发数据库级的NotNull约束报错。
最优解决方案
第一步:调整Inbox模型配置
给delegated_type关联开启autosave自动保存,同时添加关联校验规则,代码如下:
class Inbox < ApplicationRecord delegate :name, to: :inboxable # 新增autosave: true 自动处理关联对象的保存逻辑,新增validates_associated校验关联对象合法性 delegated_type :inboxable, types: %w[ Mailbox Messagebox ], dependent: :destroy, autosave: true validates_associated :inboxable # 如果Inbox本身有其他校验规则,直接正常添加即可,无需额外适配 # validates :xxx, presence: true end
配置后保存Inbox时会自动按顺序执行:校验Inbox自身属性 -> 校验关联inboxable对象属性 -> 保存inboxable -> 保存Inbox,任意一步校验失败都会终止执行,把错误汇总到Inbox的errors对象中。
第二步:调整控制器逻辑
原有控制器中先调用create再调用save属于冗余逻辑,调整为标准的Rails初始化后保存写法即可:
def create @inbox = Inbox.new(inboxable: Mailbox.new(mailbox_params)) if @inbox.save redirect_to @inbox.inboxable, notice: "<b>#{@inbox.name}</b> was created." else # 如需在视图中直接访问Mailbox的属性和错误,可额外赋值 @mailbox = @inbox.inboxable render :new end end
可选优化:合并关联对象的明细错误
默认validates_associated只会给Inbox添加一条inboxable is invalid的通用错误,如果需要直接在Inbox的错误中拿到Mailbox的字段级错误,可以给Inbox加一个校验后回调:
class Inbox < ApplicationRecord # ... 原有配置 after_validation do next unless inboxable inboxable.errors.each do |attribute, message| errors.add("inboxable.#{attribute}", message) end end end
内容的提问来源于stack exchange,提问作者Drazen
相关产品推荐
相关产品推荐

