如何让ActiveRecord的saved_change_to_attribute?在记录已更新时返回true?
解决方法
方案1:用Rails官方API强制触发变更标记
问题根源是服务B从数据库加载的user记录,其内存中的name值已经和要更新的新值一致,ActiveRecord不会自动标记变更,导致saved_change_to_name?返回false。
Rails提供了attribute_will_change!方法,能强制把指定属性标记为「待变更」,不管当前值和新值是否相同。执行更新后,回调里的saved_change_to_name?就会返回true。
代码示例:
# 服务B处理事件的代码 user = User.find(user_id) # 强制标记name属性即将变更 user.attribute_will_change!(:name) # 执行更新操作 user.update_attribute(:name, name)
这个方案用的是Rails公开API,稳定可靠,后续Rails版本升级也不容易出问题。而且等未来拆分数据库后,服务A和B各自维护自己的存储,那时A通知B的变更大概率是真的有值变化,这个代码可以直接保留,也不会有副作用。
方案2:手动修改内部变更追踪变量(不推荐)
如果出于某些原因不能用上面的方法,也可以直接操作ActiveRecord的内部变量来模拟变更,但这种方式依赖Rails的私有实现,风险较高:
user = User.find(user_id) # 手动设置changed_attributes,让ActiveRecord认为name已经变过了 user.instance_variable_set(:@changed_attributes, { name: "任意与新值不同的假旧值" }) user.update_attribute(:name, name)
不推荐这个方案的原因是,Rails内部实现如果改了,这段代码就会失效,而且其他开发者看了也容易懵。
后续拆分数据库的过渡建议
等以后拆分数据存储后,建议调整事件通知的内容:服务A更新完自己的数据库后,把旧值和新值一起传给服务B,服务B用自己数据库里的记录做更新。这时如果新旧值不一样,saved_change_to_name?会自然返回true,就不需要额外做强制标记了。
内容的提问来源于stack exchange,提问作者kylejw2
相关产品推荐
相关产品推荐

