Rails 7.2升级后Duration与PostgreSQL Interval类型异常排查
Rails 7.2升级后before_save设置的PostgreSQL Interval字段save后内存对象值为nil的解决办法
问题核心
升级Rails到7.2后,通过before_save回调设置的interval类型字段(如duration),在save!执行后内存对象中该字段为nil,但数据库已正确插入值,仅在reload后能获取到正确值。这与Rails 7.2移除的「deprecated support to quote ActiveSupport::Duration」变更直接相关。
简便解决办法
方法1:在回调中手动序列化Duration对象
修改Event模型的set_duration方法,将ActiveSupport::Duration对象序列化为PostgreSQL可识别的格式,确保内存对象保留正确值:
class Event < ApplicationRecord before_save :set_duration before_save :update_classification def set_duration duration_value = end_datetime - start_datetime # 用ActiveRecord的Interval类型序列化Duration self.duration = ActiveRecord::Type::Interval.new.serialize(duration_value) end def update_classification if duration >= 4.hours self.classification = "long" else self.classification = "short" end end end
方法2:覆盖字段赋值方法(全局生效)
如果多个模型都有类似场景,可在模型中覆盖duration=方法,统一处理Duration对象的序列化:
class Event < ApplicationRecord before_save :set_duration before_save :update_classification def duration=(value) if value.is_a?(ActiveSupport::Duration) super(ActiveRecord::Type::Interval.new.serialize(value)) else super end end def set_duration self.duration = end_datetime - start_datetime end def update_classification if duration >= 4.hours self.classification = "long" else self.classification = "short" end end end
原理说明
Rails 7.2移除了对ActiveSupport::Duration的自动引用支持,此前版本会自动将Duration对象转换为PostgreSQL Interval格式并在内存模型中保留该值;现在直接赋值Duration对象时,虽然数据库插入逻辑仍能正常序列化,但内存模型不会再维护该字段的状态,导致save!后属性为nil。通过手动序列化,我们让内存对象存储的是数据库兼容的格式,ActiveRecord会正确维护其状态,无需reload即可访问。
内容的提问来源于stack exchange,提问作者NGobin
相关产品推荐
相关产品推荐

