使用update_column更新ActiveRecord模型时如何仅保留paper-trail回调
解决paper_trail不记录
update_columns操作的实现方案 update_columns方法默认会跳过所有Active Record回调和验证,直接执行数据库更新操作,而paper_trail的版本生成逻辑依赖Active Record的after_update等回调,所以默认不会记录update_columns的变更。
方案1:重写update_columns方法自动记录版本
单模型实现
直接在已经开启paper_trail的模型中添加以下代码即可:
class YourModel < ApplicationRecord has_paper_trail def update_columns(*args) # 提取要更新的属性键值对 update_attrs = args.extract_options! args.each { |key| update_attrs[key] = public_send(key) } # 先执行原生更新逻辑,保存返回值 update_success = super # 更新成功后手动生成版本记录 if update_success && PaperTrail.enabled? && PaperTrail.request.enabled_for_model?(self.class) changeset = update_attrs.transform_values { |old_val| [old_val, public_send(_1)] } versions.create!( event: "update", object_changes: changeset, whodunnit: PaperTrail.request.whodunnit, **(PaperTrail.request.controller_info || {}) ) end update_success end end
全局多模型复用实现
如果需要给所有开启paper_trail的模型都支持该功能,可以把逻辑抽为公共关注点:
- 新建
app/models/concerns/paper_trail_update_columns_tracking.rb文件,写入以下代码:
module PaperTrailUpdateColumnsTracking extend ActiveSupport::Concern included do def update_columns(*args) update_attrs = args.extract_options! args.each { |key| update_attrs[key] = public_send(key) } update_success = super if update_success && PaperTrail.enabled? && PaperTrail.request.enabled_for_model?(self.class) changeset = update_attrs.transform_values { |old_val| [old_val, public_send(_1)] } versions.create!( event: "update", object_changes: changeset, whodunnit: PaperTrail.request.whodunnit, **(PaperTrail.request.controller_info || {}) ) end update_success end end end
- 在需要的模型中引入即可:
class YourModel < ApplicationRecord has_paper_trail include PaperTrailUpdateColumnsTracking end
方案2:手动触发版本记录(无需重写方法)
如果只有少数场景需要记录update_columns的变更,可以不用修改底层方法,每次调用后手动触发版本生成即可:
# 执行更新 your_record.update_columns(status: "completed", updated_at: Time.current) # 手动生成paper_trail版本 your_record.paper_trail.save_with_version
该方案灵活性更高,适合不需要全量覆盖的场景,缺点是容易遗漏手动调用步骤
内容的提问来源于stack exchange,提问作者Screw Ball
相关产品推荐
相关产品推荐

