Beego ORM 不支持软删除,o.Delete() 直接执行物理删除;需手动实现标记删除(Update DeletedAt)、查询过滤(Filter("DeletedAt__isnull", true))和彻底删除(Raw SQL),且 DeletedAt 必须为 *time.Time 指针并允许 NULL。

Beego ORM 本身不支持软删除,必须手动实现。 它没有内置的 SoftDelete 行为或自动拦截 Delete() 调用并转为更新状态字段的机制。
为什么 o.Delete() 会直接执行物理删除
Beego 的 orm.Delete() 方法底层调用的是 SQL DELETE FROM ... WHERE ...,不会检查模型是否有 deleted_at 字段,也不会改写成 UPDATE ... SET deleted_at = NOW()。即使你定义了 DeletedAt *time.Time `orm:"null"` 字段(如知识库中 Account 结构体所示),它也仅是普通字段,ORM 不会赋予其特殊语义。
- 所有查询(
QueryTable().All()、Read()等)默认不加WHERE deleted_at IS NULL过滤 -
Delete()操作不触发任何钩子来替换为 UPDATE - 没有类似 GORM 的
Unscoped()或WithDeleted()切换逻辑
手动实现软删除的三个关键动作
你需要同时控制「标记删除」、「查询过滤」和「彻底删除」三类行为,缺一不可:
-
标记删除:不用
o.Delete(),改用o.Update()显式设置DeletedAt字段account.DeletedAt = &now_, err := o.Update(&account, "DeletedAt") -
查询过滤:所有业务查询必须显式加条件
o.QueryTable("account").Filter("DeletedAt__isnull", true).All(&accounts)
注意 Beego ORM 的空值判断语法是字段名__isnull,不是IS NULL -
彻底删除:真要物理删时,先绕过软删逻辑(比如用原生 SQL)
o.Raw("DELETE FROM account WHERE id = ?", id).Exec()
容易踩的坑:时间字段与空值判断
Beego ORM 对 *time.Time 类型的空值处理很敏感,稍不注意就会查不到数据或报错:
-
DeletedAt *time.Time必须是 **指针类型**,否则零值time.Time{}会被存为'0001-01-01',导致__isnull判断永远为 false - 使用
Filter("DeletedAt__isnull", true)时,数据库字段必须允许为NULL(建表语句里得有deleted_at DATETIME NULL) - 如果用
auto_now或auto_now_add标签修饰DeletedAt,会导致插入/更新时被自动覆盖,失去手动控制能力 —— 所以不要加
真正麻烦的不是写几行 Update,而是整个项目里所有查询点都得补上 Filter("xxx__isnull", true),漏一处就可能返回已“删除”的脏数据。这点必须在模型设计初期就对齐团队认知,而不是等上线后补丁式修复。


















