如何在Rails的after_update回调中传递admin_id参数?
问题背景
我有一个UserProfile模型,代码如下:
class UserProfile < ApplicationRecord after_update :check_changes def check_changes AuditRecord.create(account_id: self.id, fields: self.saved_changes , account_type: 'UserProfile', admin_id: 3) if self.saved_changes? end end
UserProfile的数据表结构:
| id | user_id | name | last_name |
|---|---|---|---|
| 1 | 1 | john | doe |
| 2 | 2 | foo | bar |
同时还有一个AuditRecord模型,数据表结构:
| id | account_type | account_id | field | admin_id |
|---|---|---|---|---|
| 1 | UserProfile | 1 | {} | 3 |
| 2 | UserProfile | 2 | {} | 3 |
AuditRecord用于保存用户资料的所有更新记录,但资料可能由不同管理员操作。目前在check_changes方法中admin_id固定为3,请问如何将操作的admin_id传递给该方法?
解决方案
方法1:实例变量传递
给UserProfile添加一个临时实例变量,在更新前赋值当前管理员ID,回调中直接取用:
# 修改UserProfile模型 class UserProfile < ApplicationRecord attr_accessor :current_admin_id after_update :check_changes def check_changes return unless saved_changes? && current_admin_id.present? AuditRecord.create( account_id: id, fields: saved_changes, account_type: 'UserProfile', admin_id: current_admin_id ) end end # 业务代码中更新时传递ID user_profile = UserProfile.find(1) user_profile.current_admin_id = current_admin.id # current_admin为当前操作的管理员对象 user_profile.update(name: 'johnny')
方法2:带参数的回调(Rails 5+)
Rails 5及以上支持给回调传递参数,直接在触发更新时传入管理员ID:
# 修改UserProfile模型 class UserProfile < ApplicationRecord after_update -> { check_changes(admin_id) }, if: :saved_changes? def check_changes(admin_id) AuditRecord.create( account_id: id, fields: saved_changes, account_type: 'UserProfile', admin_id: admin_id ) end end # 业务代码中更新 user_profile = UserProfile.find(1) user_profile.update({ name: 'johnny' }, admin_id: current_admin.id)
方法3:线程局部变量(控制器全局传递)
如果操作在控制器中执行,用线程局部变量存储管理员ID,回调中直接读取:
# 管理员控制器中设置 class Admin::UserProfilesController < ApplicationController before_action :set_current_admin def update @user_profile.update(user_profile_params) end private def set_current_admin Thread.current[:current_admin_id] = current_admin.id end end # 修改UserProfile模型 class UserProfile < ApplicationRecord after_update :check_changes, if: :saved_changes? def check_changes admin_id = Thread.current[:current_admin_id] return unless admin_id.present? AuditRecord.create( account_id: id, fields: saved_changes, account_type: 'UserProfile', admin_id: admin_id ) end end
方法4:服务对象封装逻辑
把更新和审计逻辑封装到服务类中,直接传递管理员对象,避免回调参数传递的麻烦:
# 新建服务类 app/services/update_user_profile_service.rb class UpdateUserProfileService def initialize(user_profile, admin, params) @user_profile = user_profile @admin = admin @params = params end def call @user_profile.transaction do if @user_profile.update(@params) && @user_profile.saved_changes? AuditRecord.create( account_id: @user_profile.id, fields: @user_profile.saved_changes, account_type: 'UserProfile', admin_id: @admin.id ) end end end end # 业务代码中调用服务 user_profile = UserProfile.find(1) UpdateUserProfileService.new(user_profile, current_admin, { name: 'johnny' }).call
内容的提问来源于stack exchange,提问作者NaguiHW
相关产品推荐
相关产品推荐

