Beego ORM 不支持内置软删除,必须手动实现字段标记、查询拦截和禁用物理删除;调用 Delete() 会执行真实 DELETE 导致数据不可逆丢失,需添加 IsDeleted 或 DeletedAt 字段、所有查询显式过滤、禁止使用 Delete() 并确保索引优化。

Beego ORM 本身不提供内置软删除支持,必须手动实现字段标记 + 查询拦截 + 业务层约束
为什么不能直接用 Delete() 实现软删除
调用 o.Delete(&user) 会执行真实 DELETE SQL,数据不可逆丢失。软删除要求保留记录、仅逻辑标记为“已删除”,后续查询默认过滤掉这些记录。
-
Delete()底层走的是DELETE FROM ... WHERE ...,无法被 ORM 自动重写为 UPDATE - 没有类似 Django 的
is_active默认过滤钩子,也不会自动在所有QueryTable()后追加IsDeleted = false - 即使你加了
IsDeleted bool字段,不显式写条件,查出来的结果里仍包含已“删”数据
手动实现软删除的三个必要动作
缺一不可:加字段、改查询、封禁物理删除
- 在模型中添加软删除字段,例如:
IsDeleted bool `orm:"default(false)"`或更推荐用时间戳:DeletedAt *time.Time `orm:"null"` - 所有正常业务查询必须显式排除已删数据:
o.QueryTable(&User{}).Filter("is_deleted", false).All(&users);若用DeletedAt,则写Filter("deleted_at__isnull", true) - 禁止在业务代码中调用
o.Delete()—— 可以在项目初始化时用go vet或自定义 linter 检查,或封装一个SoftDelete()方法替代
Filter() 中的字段名和语法陷阱
Beego ORM 的 Filter() 不认 Go 字段名,只认数据库列名或结构体 tag 中定义的 column 名
- 如果结构体字段是
IsDeleted bool `orm:"column(is_deleted);default(false)"`,那Filter()必须写"is_deleted",不能写"IsDeleted" - 时间戳字段若用
DeletedAt *time.Time `orm:"column(deleted_at);null"`,判断未删除应写Filter("deleted_at__isnull", true)(注意双下划线) - 错误写法:
Filter("IsDeleted", false)→ 查不到任何数据,因为 ORM 找不到叫IsDeleted的列
事务中软删除与关联数据的一致性
软删除不是原子操作,如果你还依赖外键或级联行为(比如用户软删后其订单也需软删),得自己写逻辑
- ORM 不支持
ON DELETE SET NULL或CASCADE对软删除字段生效,这些仍是数据库层面的物理约束 - 推荐做法:在
SoftDeleteUser()函数内,先更新User.IsDeleted = true,再批量更新关联表的user_id对应记录的is_deleted字段 - 务必用
orm.RunTransaction()包裹,否则部分成功会导致状态不一致
真正容易被忽略的是:软删除字段的索引缺失和查询性能。一旦 is_deleted 字段没建索引,随着数据增长,Filter("is_deleted", false) 会越来越慢 —— 别只顾着加字段,忘了在数据库里跑 ALTER TABLE users ADD INDEX idx_is_deleted (is_deleted)。


















