如何在PaperTrail中用关联名称替换审计日志里的ID?
问题
在关联操作中,将其他模型的ID分配给当前模型时,PaperTrail会追踪ID变更,生成的object_changes数据如下:
{ "updated_at": [ "2023-11-13T08:54:26.346Z", "2023-11-13T08:56:06.961Z" ], "paying_id": [ "ID1", "ID1 new" ], "company_ids": [ [ "ID1", "ID2", "ID3", "ID4" ], [ "ID1 new", "ID2 new", "ID3 new" ] ] }
现在需要在审计日志视图中显示ID对应的名称而非ID本身,当前用循环查询名称再传给视图的方案不够优化,且官方不建议重写object_changes,请问正确实现方式是什么?
解决方案
1. 利用PaperTrail的metadata存储关联名称(推荐)
在创建版本记录时,主动将关联模型的名称存入版本的metadata字段,避免后续重复查询:
- 在模型的
paper_trail配置中,通过meta选项添加自定义数据:
class YourModel < ApplicationRecord has_paper_trail meta: lambda { |model| { paying_name: model.paying&.name, company_names: model.companies.pluck(:name) } } end
- 更新关联时,PaperTrail会自动把当前的关联名称存入版本的
metadata,视图中直接读取version.metadata["paying_name"]等字段即可,无需额外查询。
2. 视图层封装辅助方法批量查询
避免循环单条查询,改用批量查询减少数据库请求:
- 在视图辅助方法中,批量获取所有需要转换的ID对应的名称:
# app/helpers/audit_log_helper.rb def map_ids_to_names(ids, model_class) records = model_class.where(id: ids).pluck(:id, :name) records.to_h end
- 在视图中使用时,先批量获取ID映射,再替换显示:
<% paying_id_map = map_ids_to_names([version.object_changes["paying_id"][0], version.object_changes["paying_id"][1]], PayingModel) %> 变更前: <%= paying_id_map[version.object_changes["paying_id"][0]] %> 变更后: <%= paying_id_map[version.object_changes["paying_id"][1]] %> <% company_id_map = map_ids_to_names(version.object_changes["company_ids"].flatten, Company) %> 变更前公司: <%= version.object_changes["company_ids"][0].map { |id| company_id_map[id] }.join(", ") %> 变更后公司: <%= version.object_changes["company_ids"][1].map { |id| company_id_map[id] }.join(", ") %>
这种方式把N次查询减少到2次,性能更优。
3. 为版本记录添加关联查询方法
在Version模型中封装方法,直接返回关联名称:
class Version < PaperTrail::Version def paying_name_before PayingModel.find_by(id: object_changes["paying_id"][0])&.name end def paying_name_after PayingModel.find_by(id: object_changes["paying_id"][1])&.name end def company_names_before Company.where(id: object_changes["company_ids"][0]).pluck(:name).join(", ") end def company_names_after Company.where(id: object_changes["company_ids"][1]).pluck(:name).join(", ") end end
- 视图中直接调用这些方法:
变更前付款方: <%= version.paying_name_before %> 变更后付款方: <%= version.paying_name_after %>
注意:如果历史ID对应的记录已被删除,find_by会返回nil,需处理空值情况。
内容的提问来源于stack exchange,提问作者FiFi
相关产品推荐
相关产品推荐

