You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails 6.0 ActiveRecord软删除default_scope覆盖STI查询问题

问题根因

故障核心出在原有软删除concern的两处实现:

  • not_deleted scope里调用了base.unscoped,这个方法会清空当前查询链上所有已挂载的默认条件,包括Rails为STI模型自动追加的type过滤规则,不会走官方文档说明的default_scope AND合并逻辑——因为unscoped直接把前置的scope全清掉了。
  • default_scope硬绑定了base(也就是include concern的类)的上下文,STI子类继承时不会动态适配子类的查询上下文,后续手动加type scope时也因为上下文提前绑定,导致type值固定为基类名。

方案1:完全不修改原有软删除concern的修复方式

直接在STI基类中重写查询构造的底层入口relation方法,动态追加type过滤条件即可,完全不需要改动concern代码,其他使用该concern的存量非STI模型不受任何影响:

class Widget < ApplicationRecord
  include ActiveRecordSoftDeletion

  # 所有查询构造都会走该方法,动态匹配当前调用类的STI类型
  def self.relation
    super.where(type: sti_name)
  end
end

实现逻辑:

  • 不管default_scope里的unscoped怎么清空条件,最终生成查询时都会经过relation方法,这里追加的条件不会被清掉
  • 方法内的self是实际发起查询的类:Thingy.first调用时self是Thingy,自动追加type = 'Thingy';Dohicky.first调用时self是Dohicky,自动追加type = 'Dohicky';基类Widget.first调用时追加type = 'Widget',完全符合STI的预期行为。

方案2:修改软删除concern的兼容实现(零影响存量模型)

如果允许调整concern代码,可以从根源修复STI兼容问题,且所有存量非STI模型的行为和之前完全一致,不需要做任何业务代码改动。
修改后的concern代码如下:

module ActiveRecordSoftDeletion
  extend ActiveSupport::Concern

  included do
    scope :not_deleted, -> { unscope(where: :deleted).where(deleted: false) }
    scope :only_deleted, -> { unscope(where: :deleted).where(deleted: true) }

    default_scope { not_deleted }

    def destroy
      self.deleted = true
      self.save(validate: false)
    end

    def recover
      self.deleted = false
      self.save(validate: false)
    end
  end
end

调整点说明:

  1. 把原来scope里的base.unscoped替换为unscope(where: :deleted):原来的unscoped会清空所有查询条件(包括STI的type、其他模型自定义的default_scope),改成只清除deleted字段的过滤条件,既避免了链式调用时deleted条件重复叠加,又不会误删其他必要的默认条件。
  2. 去掉default_scope和scope定义里硬编码的base.前缀:scope块执行时的self会自动指向当前发起查询的类(STI场景下就是子类),Rails会自动追加STI要求的type过滤条件,不会出现type值固定为基类的问题。
  3. 实例方法destroy、recover逻辑完全保留,存量业务的删除、恢复调用不需要做任何修改。

内容的提问来源于stack exchange,提问作者Doug Couvillion

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 05:21:38