models.py臃肿因将校验、状态流转等业务逻辑塞入save()等方法;应只保留字段派生、简单级联等单记录完整性逻辑,外部调用和复杂流程移交无状态、按业务域划分的service层。

为什么 models.py 越来越臃肿?
因为把校验、状态流转、第三方调用、批量计算全塞进 save()、delete() 或自定义方法里了。模型本该只管「数据结构」和「基础约束」,比如 CharField(max_length=100)、ForeignKey(on_delete=models.CASCADE)。一旦出现 send_email()、calculate_score()、sync_to_erp() 这类调用外部服务或含多步判断的逻辑,就该警觉:它正在变胖。
Service 层怎么组织才不变成新包袱?
Service 不是建个 services/ 目录扔一堆函数就行。关键在职责边界和调用入口清晰:
-
service模块按业务域分,不是按模型分 —— 比如order_service.py处理订单创建、支付回调、发货等全流程,而非user_service.py里塞所有用户相关操作 - 函数必须是无状态的:只依赖传入参数,不读写 Django 的
request或session(那是视图层的事) - 拒绝
def create_order(user, items, address)这种大参数列表 —— 改用OrderCreationInput数据类封装输入,方便校验和复用 - 所有数据库操作仍走 ORM,但由 service 控制事务边界:
with transaction.atomic():包裹整个业务流程,而不是靠模型的save()自发提交
save() 里还能留什么?
只保留与「单条记录自身完整性」强相关的逻辑:
- 字段派生值:比如
slug = slugify(self.title)、updated_at = timezone.now() - 简单级联:如
status变为'archived'时自动设archived_at - 不能有查询其他模型、发 HTTP 请求、触发信号(
post_save也得慎用) - 如果连
full_clean()都被绕过(比如 admin 中form.save(commit=False)),那更不该在save()里做业务校验
典型反例:save() 里调用 payment_gateway.charge() —— 这会导致测试困难、事务失控、重试逻辑混乱。
怎么让团队接受并持续用好 Service 层?
靠文档和工具约束,不是靠口头约定:
- 在
models.py顶部加注释模板:# ✅ OK: slug generation, updated_at update/# ❌ NO: API calls, cross-model updates, business rules - CI 中加检查:用
grep -r "requests\|httpx\|urllib\|send_mail\|celery" models/报警 - Pydantic
BaseModel定义 service 输入输出,IDE 能自动补全字段,比 dict 字符串键安全得多 - admin 或 DRF serializer 中,把原来直接调模型方法的地方,改成调
order_service.create_order(...)—— 让 service 成为唯一业务入口
最难的不是写 service,而是忍住不在 views.py 里临时加三行业务代码。每次破例,模型就又长出一斤脂肪。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











