Ruby on Rails中如何编写两类Audit对应的named scope?
嘿,针对你在Ruby on Rails里使用Audit gem的两个需求,我整理了对应的named scope写法,你可以直接放到Audit模型里用:
1. 获取无关联Auditable的Audit记录
这个scope会覆盖两种情况:要么Audit从未关联过任何Auditable模型,要么曾经关联的Auditable已经被删除了。
class Audit < ApplicationRecord belongs_to :auditable, polymorphic: true, optional: true scope :without_auditable, -> { # 匹配本身就没有关联的记录 where(auditable_id: nil) .or( # 匹配关联的Auditable已被删除的记录 where.not(auditable_type: nil).where.not( exists( Arel::Table.new(auditable_type.tableize).unscoped.where( id: arel_table[:auditable_id] ) ) ) ) }
如果你的Auditable类型不多,也可以用更直观的left join版本:
scope :without_auditable, -> { left_joins(:auditable) .where(auditable: { id: nil }) }
解释:第一种写法用exists子查询,能适配所有类型的Auditable模型;第二种left join方式更易读,Rails会自动处理多态关联的join逻辑,同样能得到正确结果。
2. 获取关联Order未被删除的Audit记录
这个scope专门筛选那些关联的Order仍然存在(没被删除)的Audit记录:
scope :with_existing_order, -> { where(auditable_type: "Order") .where( exists( Order.unscoped.where( id: arel_table[:auditable_id], deleted_at: nil # 用软删除(比如paranoia)就保留这行,硬删除则删掉 ) ) ) }
解释:
- 先通过
auditable_type: "Order"锁定所有关联Order的Audit; - 用
exists子查询确保对应的Order记录存在,unscoped是为了绕过Order模型上的默认scope,避免过滤掉有效记录; - 要是用的是硬删除(直接destroy,无deleted_at字段),去掉
deleted_at: nil这一行即可。
内容的提问来源于stack exchange,提问作者Umesh Malhotra




