0

0

sqlalchemy 如何写“软删除”并在查询中自动过滤已删记录

冰川箭仙

冰川箭仙

发布时间:2026-01-24 19:14:02

|

301人浏览过

|

来源于php中文网

原创

软删除是逻辑标记而非物理移除,仅用 is_deleted 字段不够,需全局拦截查询、关联加载及事件监听以确保所有数据入口均尊重该标记。

sqlalchemy 如何写“软删除”并在查询中自动过滤已删记录

什么是软删除,为什么不能只靠 is_deleted 字段

软删除本质是逻辑标记而非物理移除,但光加个 is_deleted 布尔字段远远不够。SQLAlchemy 不会自动识别这个字段的语义,所有查询(包括 session.query(Model)Model.query.all()、关联加载)默认都会拉出已标记删除的记录。必须显式干预查询构建过程,否则“软”就变成了“假装删了”。

常见错误现象:model.delete() 后查出来还在;外键关联对象仍能加载已软删的父记录;admin 后台列表里出现“已删除”的脏数据。

用 query_class + 自定义 Query 实现全局过滤

最直接可控的方式是重写模型的默认查询类,让每次 Model.querysession.query(Model) 都自动加上 is_deleted == False 条件。

实操建议:

  • 定义一个继承 Query 的子类,在 __iter__all() 调用前统一 apply filter
  • 在模型中通过 query_class 属性指定该类
  • 确保 is_deleted 字段类型为 Boolean,且有默认值 False
class SoftDeleteQuery(Query):
    def __iter__(self):
        return super().filter(self._mapper_zero().is_deleted == False).__iter__()

class User(Base): tablename = 'users' id = Column(Integer, primary_key=True) name = Column(String(50)) is_deleted = Column(Boolean, default=False) query_class = SoftDeleteQuery

注意:这种方式对 session.query(User) 无效(它不走 User.query),需配合 session.query(User).with_polymorphic('*') 或改用 User.query 风格。

用 SQLAlchemy 2.0+ 的 select() + with_only_columns 替代旧式 query

SQLAlchemy 2.0 推荐使用 select() 构建查询,此时无法复用 query_class。需手动封装一个带过滤的查询构造器,或借助 Event 拦截 select()

更稳妥的做法是定义一个类方法:

墨鱼aigc
墨鱼aigc

一款超好用的Ai写作工具,为用户提供一键生成营销广告、原创文案、写作辅助等文字生成服务。

下载
class User(Base):
    # ... 字段定义同上
    @classmethod
    def not_deleted(cls):
        return select(cls).where(cls.is_deleted == False)

使用:

stmt = User.not_deleted().where(User.name.contains('john')) result = session.execute(stmt).scalars().all()

这样既清晰又可控,避免隐式行为。如果项目已大量使用 select(),硬塞 query_class 反而增加理解成本。

外键关联和 joinedload 时如何保持软删除语义

软删除最大的坑不在主表,而在关联——比如查订单时,joinedload(Order.user) 会把已软删的用户也一起加载进来,破坏业务一致性。

解决方案分两层:

  • 对一对一/多对一外键,在关系定义中加 primaryjoin 条件,例如:user = relationship('User', primaryjoin='and_(Order.user_id == User.id, User.is_deleted == False)')
  • 对一对多(如 User.orders),在 relationship 中加 viewonly=True + 单独定义一个 not_deleted_orders 属性,用 select() + where 显式过滤
  • 避免在 lazy='joined' 场景下依赖默认关联,容易漏掉 is_deleted 判断

真正难处理的是嵌套三层以上的关联(比如 Order → Item → Supplier),这时候推荐用 CTE 或物化视图预过滤,而不是靠 ORM 动态拼条件——性能和可读性都会迅速恶化。

软删除不是加个字段就完事,关键在于所有数据入口(CRUD、关联、批量操作、raw SQL)是否都尊重这个标记。最容易被忽略的是事件监听器(如 @event.listens_for(User, 'before_insert'))和 Alembic 迁移脚本里的初始数据插入——它们常常绕过模型层逻辑,直接写入 is_deleted = True 却没配好默认值或索引。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

686

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

324

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

348

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

1137

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

359

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

737

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

577

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

420

2024.04.29

c++ 根号
c++ 根号

本专题整合了c++根号相关教程,阅读专题下面的文章了解更多详细内容。

25

2026.01.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Java 教程
Java 教程

共578课时 | 50.4万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号