Rails 6.1+中使用destroy_async时如何为PaperTrail传递whodunnit?
问题描述
我在基于Rails 7.2的应用中使用PaperTrail gem追踪模型变更(包括删除操作),希望用上Rails 6.1引入的destroy_async特性。后台删除功能正常,但异步执行销毁时,PaperTrail无法捕获whodunnit。
我试过用PaperTrail.request(whodunnit: current_user)包裹销毁调用:
PaperTrail.request(whodunnit: current_user) do object.destroy end
但使用destroy_async时没法正确关联whodunnit。我还试过自定义后台处理删除并显式传递whodunnit,虽然可行,但不如内置的destroy_async简洁。
请问有没有清晰的方法,在使用destroy_async时为PaperTrail传递whodunnit?
解决方案
扩展destroy_async传递whodunnit
destroy_async本质是通过ActiveJob执行异步销毁,我们可以扩展模型的destroy_async方法,将当前上下文的whodunnit传入Job,再在Job执行时为PaperTrail设置该值,既保留内置方法的简洁性,又能正确记录操作人。
- 创建一个Concern文件(
app/models/concerns/paper_trail_destroy_async.rb):
module PaperTrailDestroyAsync extend ActiveSupport::Concern included do # 重写destroy_async,支持传递whodunnit参数 def destroy_async(whodunnit: PaperTrail.request.whodunnit) self.class.destroy_async_job_class.new(id, whodunnit: whodunnit).enqueue end # 自定义销毁Job,执行时设置PaperTrail的whodunnit class DestroyAsyncJob < ActiveJob::Base def perform(record_id, whodunnit: nil) record = self.class.model_class.find(record_id) PaperTrail.request(whodunnit: whodunnit) do record.destroy end end private def self.model_class @model_class ||= name.deconstantize.constantize end end end end
- 在需要使用的模型中引入该Concern:
class Post < ApplicationRecord include PaperTrailDestroyAsync has_paper_trail end
- 确保控制器上下文已设置
whodunnit(可在ApplicationController全局配置):
class ApplicationController < ActionController::Base before_action :set_paper_trail_whodunnit private def set_paper_trail_whodunnit # 这里用current_user的ID或邮箱作为whodunnit标识,按需调整 PaperTrail.request.whodunnit = current_user&.id.to_s end end
- 直接调用
destroy_async即可自动传递whodunnit:
# 控制器中调用 @post.destroy_async # 也可显式指定whodunnit(覆盖上下文值) @post.destroy_async(whodunnit: admin_user.id)
原理说明
- 重写的
destroy_async会捕获当前PaperTrail请求上下文的whodunnit,作为参数传入自定义的DestroyAsyncJob。 - Job执行时,通过
PaperTrail.request包裹销毁操作,临时设置whodunnit,让PaperTrail能正确记录该异步操作的发起者。
内容的提问来源于stack exchange,提问作者steimo
相关产品推荐
相关产品推荐

