起因
Django 和 Django REST framework 是 Python 开发者常用的框架组合,通常来说,一个典型的 DRF 式 API 可能长这个样子:
1 | from rest_framework.generics import ListAPIView |
这样写在逻辑上是简单的,可以让开发者对用户请求处理有一个清晰的脉络,但同时也会带来问题:Serializer
的逻辑和主逻辑混杂,使单元测试构造困难。
同时,输入输出的代码在多个 API 中是有一定程度重复的, D.R.Y
重度患者无法接受。
启发
新贵框架 FastAPI 的 依赖注入特性 就能够很好的解决以上两点:
1 | from fastapi import Depends, FastAPI |
然而,现实中的工程中切换框架往往是成本高昂的,仅仅为了依赖注入而切换框架显得有些小题大做。所以,如果能在 Django & DRF 中实现类似依赖注入的功能,会较大程度提高 views
的可读性并降低 TDD 的门槛,间接提高代码质量。
同时我们需要满足几个条件:
- 能够兼容当前的 ViewSet 类
- 能够复用 Serializer
- (可选)能够复用
drf-yasg
综上,我写了一个 简单的文件 ,你可以将它 Copy 到你的 DRF 项目中就可以改造原来的 ViewSet
(当前需求是比较简单的,封装成 SDK 然后安装依赖的成本反而高于直接复制粘贴,这样大家可以一起偷懒)
最后的效果:
原来的 ViewSet
(包含 drf-yasg
的 schema 生成)
1 | class ProfileViewSet(ListAPIView): |
改造之后的效果:
1 | from some_path.inject import serializer_inject |
(可以通过 gist 评论 获取更多的例子)
这样的改造我们得到了一些好处:
- 仅需要简单改造原来的
ViewSet
- 完全继承原来的
Serializer
- 完整支持
drf-yasg
- 在原来主干逻辑没有依赖
request
对象的情况下,单元测试的用例构造被简化成了dict
当然仍旧还有不完美的地方:
- 没有使用
Type Annotation
,在声明上较FastAPI
更为冗余 - 对于返回值使用了
context
的Serializer
需要通过inject.ResponseParams
类来包装一次,显得不那么纯粹,暂时也没有更好的思路,有空再慢慢改(咕咕🐦)。