Rails应用中PaperTrail无法同时存储控制器与模型级元数据的问题求助
Rails PaperTrail 元数据存储问题:同时保存控制器与模型级元数据
问题场景
在Rails应用中使用PaperTrail时,尝试将控制器级(app_id、org_id)和模型级(acted_user)元数据同时存入versions表,但只有控制器级元数据被成功保存,模型生成的acted_user详情未被存储。已确认PaperTrail.request.controller_info包含正确的:metadata值。
现有配置代码
模型文件(Table类)
class Table < ApplicationRecord self.table_name = 'tablename ' has_paper_trail meta: { metadata: lambda { |record| user = record.admin_apps_orgs_user.user acted_user = { id: user.id, name: "#{user.given_name} #{user.family_name}", email: user.email } controller_metadata = PaperTrail.request.controller_info.try(:[], :metadata) || {} { app_id: controller_metadata[:app_id], org_id: controller_metadata[:org_id], acted_user: acted_user } } } end
控制器辅助模块
# frozen_string_literal: true module PapertrailInfoHelper extend ActiveSupport::Concern def info_for_paper_trail { metadata: { app_id: params[:app_id], org_id: params[:org_id] } } end end
控制器文件
module Api module V1 class MyController < ApplicationController include PapertrailInfoHelper # Controller actions end end end
解决方案
核心问题是模型定义的meta哈希与控制器info_for_paper_trail返回的哈希结构冲突:两者都使用:metadata作为顶级键,导致控制器的:metadata覆盖了模型生成的同名键值。以下两种调整方式均可解决问题:
方法1:直接合并元数据(推荐)
修改模型配置,让模型生成的元数据与控制器元数据直接合并,不再嵌套在:metadata下:
class Table < ApplicationRecord self.table_name = 'tablename' has_paper_trail meta: lambda { |record| # 安全获取关联用户,避免nil报错 user = record.admin_apps_orgs_user&.user return {} unless user acted_user = { id: user.id, name: "#{user.given_name} #{user.family_name}", email: user.email } # 合并控制器传递的元数据 controller_metadata = PaperTrail.request.controller_info || {} controller_metadata.merge(acted_user: acted_user) } end
同时调整控制器辅助模块,直接返回元数据键值对:
module PapertrailInfoHelper extend ActiveSupport::Concern def info_for_paper_trail { app_id: params[:app_id], org_id: params[:org_id] } end end
方法2:保留嵌套结构并手动合并
若需保留:metadata嵌套结构,修改模型中的lambda逻辑,将控制器与模型的元数据合并到同一:metadata键下:
class Table < ApplicationRecord self.table_name = 'tablename' has_paper_trail meta: { metadata: lambda { |record| user = record.admin_apps_orgs_user&.user return {} unless user acted_user = { id: user.id, name: "#{user.given_name} #{user.family_name}", email: user.email } controller_metadata = PaperTrail.request.controller_info.dig(:metadata) || {} # 合并两类元数据 controller_metadata.merge(acted_user: acted_user) } } end
控制器代码可保持不变,最终versions表的meta字段会包含一个:metadata键,其值包含所有目标数据。
验证要点
- 确保
record.admin_apps_orgs_user&.user不会返回nil,避免NoMethodError - 执行更新操作后,检查
versions表的meta字段,确认app_id、org_id、acted_user均已存入
内容的提问来源于stack exchange,提问作者Mohan Mali
相关产品推荐
相关产品推荐

