Ruby on Rails中如何筛选Auditable非空的Audit记录?
解决Auditable为nil的Audit记录过滤问题
嘿,这个场景我之前做项目的时候也碰到过——删除关联实体后,Audit记录还留在库里,但auditable就变成nil了。这里有几种实用的解决办法,你可以根据需求选:
方法1:用Arel构建安全的存在性查询
因为Audit是多态关联,我们可以通过检查auditable_type和auditable_id是否在对应实体表中存在来过滤。用Arel写既安全又灵活,不会有SQL注入风险:
# 构建Order和Invoice的存在性条件 orders_condition = Order.arel_table[:id].eq(Audit.arel_table[:auditable_id]) .and(Audit.arel_table[:auditable_type].eq('Order')) invoices_condition = Invoice.arel_table[:id].eq(Audit.arel_table[:auditable_id]) .and(Audit.arel_table[:auditable_type].eq('Invoice')) # 获取所有关联实体仍存在的Audit记录 valid_audits = Audit.where(orders_condition.or(invoices_condition))
之后新增其他Auditable模型时,照着格式加新的condition就行,扩展性很强。
方法2:给Audit模型封装可复用的Scope
如果需要频繁用到这个过滤逻辑,把它做成Audit模型的Scope会更整洁:
# 在app/models/audit.rb中添加 class Audit < ApplicationRecord scope :with_existing_auditable, -> { # 列出所有需要检查的Auditable模型 auditable_classes = [Order, Invoice] # 用Arel拼接所有存在性条件 conditions = auditable_classes.map do |klass| klass.arel_table[:id].eq(Audit.arel_table[:auditable_id]) .and(Audit.arel_table[:auditable_type].eq(klass.name)) end.reduce(:or) where(conditions) } end
之后调用起来就很简单:
valid_audits = Audit.with_existing_auditable
方法3:预防式方案(软删除)
如果不想之后再处理这类问题,可以给Order、Invoice这类模型用软删除(比如paranoia或Rails自带的discard gem)。删除实体时只是标记为已删除,不会真正从数据库移除,对应的Audit记录的auditable就永远不会变成nil了。不过这个方案更适合还没大量硬删除实体的项目,要是已经有很多已删除的记录,还是前面的过滤方法更直接。
内容的提问来源于stack exchange,提问作者Umesh Malhotra
相关产品推荐
相关产品推荐

