Rails提供记录ID仍无法更新field_values嵌套属性问题求助
问题原因
- 核心问题是ActiveRecord的脏检测机制不会识别
attr_accessor定义的虚拟属性变更:你更新嵌套字段时仅传入了虚拟属性value,ActiveRecord判断该FieldValue记录没有数据库字段发生变更,直接跳过了更新流程,因此before_validation回调set_value完全不会被执行,自然不会更新实际存储的text_value/number_value等字段。 - 创建和删除操作不受影响是因为:创建时会触发全流程保存,删除时内置的
_destroy参数会被ActiveRecord识别为变更操作直接执行删除逻辑。
解决方案
方案1:将虚拟属性替换为Active Record非持久化属性(推荐,Rails 5+支持)
把FieldValue模型里的attr_accessor :value替换为attribute :value,该方法定义的属性会被纳入脏检测范围,只要value发生变更就会触发完整的更新流程。
# FieldValue.rb class FieldValue < ApplicationRecord # 替换原有的attr_accessor :value attribute :value belongs_to :field, class_name: 'FieldInput', foreign_key: 'field_component_id' before_validation :set_value private def set_value case field.field_type.key when 'text' self.text_value = value when 'number' self.number_value = value when 'date' else self.string_value = value end end end
方案2:手动标记记录为变更状态
如果使用低版本Rails不支持attribute方法,可以自定义value的setter方法手动标记记录需要保存:
# FieldValue.rb class FieldValue < ApplicationRecord attr_accessor :value # 自定义setter,触发脏标记 def value=(val) @value = val # 手动标记记录已变更,强制触发更新流程 will_save_change_to_attribute?(:value) end belongs_to :field, class_name: 'FieldInput', foreign_key: 'field_component_id' before_validation :set_value # 其余代码不变 end
方案3:在嵌套属性配置中强制触发更新
如果你的嵌套记录更新场景固定,也可以给accepts_nested_attributes_for添加配置,强制每次传入嵌套属性都执行更新逻辑:
# ExecutionStep.rb class ExecutionStep < ApplicationRecord has_many :field_values, class_name: 'FieldValue', as: :valueable, dependent: :destroy enum status: { not_started: 0, in_progress: 1, paused: 3, retry: 4, completed: 5, failed: 6, canceled: 7 } # 添加update_only: true强制触发更新 accepts_nested_attributes_for :field_values, allow_destroy: true, update_only: true end
注意该方案会跳过变更检测,无论字段有没有变化都会执行更新,性能略低于前两种方案。
内容的提问来源于stack exchange,提问作者Miroslav
相关产品推荐
相关产品推荐

