前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >让 DRF Views 支持依赖注入

让 DRF Views 支持依赖注入

作者头像
布鲁斯鱼
发布2022-11-02 14:07:31
3850
发布2022-11-02 14:07:31
举报
文章被收录于专栏:布鲁斯鱼的妙想天开

起因

Django 和 Django REST framework 是 Python 开发者常用的框架组合,通常来说,一个典型的 DRF 式 API 可能长这个样子:

代码语言:javascript
复制
from rest_framework.generics import ListAPIView


class ProfileViewSet(ListAPIView):
    def login(self, request):
        serializer = LoginSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        validated_data = serializer.validated_data

        ...
        return Response(data=ProfileSerializer(results, many=True).data)

这样写在逻辑上是简单的,可以让开发者对用户请求处理有一个清晰的脉络,但同时也会带来问题:Serializer 的逻辑和主逻辑混杂,使单元测试构造困难。

同时,输入输出的代码在多个 API 中是有一定程度重复的, D.R.Y 重度患者无法接受。

启发

新贵框架 FastAPI依赖注入特性 就能够很好的解决以上两点:

代码语言:javascript
复制
from fastapi import Depends, FastAPI

app = FastAPI()

@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    return commons

然而,现实中的工程中切换框架往往是成本高昂的,仅仅为了依赖注入而切换框架显得有些小题大做。所以,如果能在 Django & DRF 中实现类似依赖注入的功能,会较大程度提高 views 的可读性并降低 TDD 的门槛,间接提高代码质量。

同时我们需要满足几个条件:

  • 能够兼容当前的 ViewSet 类
  • 能够复用 Serializer
  • (可选)能够复用 drf-yasg

综上,我写了一个 简单的文件 ,你可以将它 Copy 到你的 DRF 项目中就可以改造原来的 ViewSet

(当前需求是比较简单的,封装成 SDK 然后安装依赖的成本反而高于直接复制粘贴,这样大家可以一起偷懒

最后的效果:

原来的 ViewSet (包含 drf-yasg 的 schema 生成)

代码语言:javascript
复制
class ProfileViewSet(ListAPIView):
    @swagger_auto_schema(
        request_body=LoginSerializer,
        responses={status.HTTP_200_OK: ProfileSerializer()},
    )
    def login(self, request):
        serializer = LoginSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        validated_data = serializer.validated_data

        ...
        return Response(data=ProfileSerializer(results, many=True).data)

改造之后的效果:

代码语言:javascript
复制
from some_path.inject import serializer_inject


class ProfileViewSet(ListAPIView):
    @serializer_inject(
        in_cls=LoginSerializer,
        out_cls=ProfileSerializer,
        # 选择去掉原有的 request 依赖
        config={"remain_request": False},
        out_params={"many": True},
    )
    def login(self, validated_data: dict):
        # 原来的逻辑部分
        ...
        return results

(可以通过 gist 评论 获取更多的例子)

这样的改造我们得到了一些好处:

  • 仅需要简单改造原来的 ViewSet
  • 完全继承原来的 Serializer
  • 完整支持 drf-yasg
  • 在原来主干逻辑没有依赖 request 对象的情况下,单元测试的用例构造被简化成了 dict

当然仍旧还有不完美的地方:

  • 没有使用 Type Annotation ,在声明上较 FastAPI 更为冗余
  • 对于返回值使用了 contextSerializer 需要通过 inject.ResponseParams 类来包装一次,显得不那么纯粹,暂时也没有更好的思路,有空再慢慢改(咕咕🐦)。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021 年 5 月 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 起因
  • 启发
  • 最后的效果:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档