浅谈 Django-REST-Framework 的设计与源码

浅谈 Django-REST-Framework 的设计与源码 Recommended

10月 20, 2016
Python, Django, Recommended, 软件工程, 源码分析

最近又重新接触 DRF,翻看文档发现,当时很难理解的东西,如今一看就懂了,顺带看了源码,也比较容易理解,至少比 Django 的源码简单不少。下面开始从 DRF 的设计和源码两个方面,结合自己的看法,谈谈DRF。

APIView #

APIView 是 DRF 概念体系中最基本类视图,它基于 Django 的 View,同时又实现了一层对自己创造的概念的封装,比如permission_classesrender_classesauthentication_classes等,相当于请求过来后又进入一层,过滤(验证、初始化)完要走的这些classes,才能执行到我们自己实现的 view。

Mixin #

在介绍 Generic view 前先说一下 mixin。说白了,官方给的案例里面的 mixin,就是对 model 的object增删改查的原子操作。比如RetrieveModelMixin,通过 get_object 先获取到对象,然后通过 serializer 拿到序列化数据。

class RetrieveModelMixin(object):
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

延伸开来,Mixin 是一个非常重要的概念,因为它是模块化拆分后最关键的原子逻辑实现,是每一个单独的块。试想下,我们可以通过这些小块拼接成不同的功能,比如用户注册功能,使用者有可能在手机注册,有可能在web注册,在 Django 后端中可能需要提供两套 view 层接口,甚至还有渲染 template 的实现,但是真正的逻辑处理上,可以只调用一个RegistMixin的东西,从而提高代码复用,降低维护成本。

甚至,Mixin 可以重写同时继承的 GenericAPIView 中的实现,比如 request 参数处理,Response 数据处理(视频、文件格式之类),get_queryset,get_serializer 等等有通用需求,并且会在多个地方复用的定制,比如根据传入参数指定需要的 serializer 从而在 mixin 中重写 get_serializer 方法的实现逻辑。总之,原本会在 View 中发生的一切,都可以通过 Mixin 封装起来,或重写、或新的逻辑实现。

Generic views #

典型的Generic view就是这样的,本质上是类视图,它由一些 Mixin 组成,这里的ListModelMixin实现了list()方法,并同时继承自GenericAPIView,同时自己实现了由 get() 到 list() 的转发。复杂的是,GenericAPIView 实现,以及他的执行过程,和后期对里面函数的重写、定制。所以抽时间多研究下这里很值得,以后也会节省大量时间。

class ListAPIView(mixins.ListModelMixin,
                  GenericAPIView):
    """
    Concrete view for listing a queryset.
    """
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

如果要实现的逻辑不是很复杂的话,直接在view中继承一个[List[Create[Update]]]APIView,然后指定下serializer_classpermission_classqueryset等就可以了。对DRF熟悉的话相信这种方式实现功能会很快。

Viewsets #

先看代码,ViewSet 继承了ViewSetMixinAPIViewViewSetMixAPIViewas_view的重写,实现请求到 action 的转发,虽然 Django 自带的 View 也实现了 as_view,但只是传递初始化参数,确实没有这个请求转发到 action 的实现,APIView里面也没有。这里也可以学习到:Python的多重继承中,子类形参的先后顺序,影响到执行的是哪个类中的as_view方法,相当于ViewSetMixin又继承了APIView

class ViewSet(ViewSetMixin, views.APIView):
    """
    The base ViewSet class does not provide any actions by default.
    """
    pass

viewsets 和 APIView 不一样的地方在于,它要表达的更多的是一个方法视图的集合,他的内部是已经实现了的list()retrieve()delete()update()的实现,如果实现用 DRF 的 DefaultRouter。router和as_view都可以实现GET请求–>list()的转发,值得一提的还有detail_routelist_route(最新版本推荐使用action),他们需要在urls中配置 router 的前提下使用,在 Router 类的get_url里面的get_routers执行,然后获得所有的路由列表,再进行 action 转发。

# account/views.py
class AccountViewSet(viewsets.ModelViewSet):
    """
    对Account的操作的集合,本质上是基于方法视图的封装
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @detail_route(methods=['post'], url_path='change-password')
    def set_password(self, request, *args, **kwargs):
        """
        修改用户的密码
        """
        return Response(status=status.HTTP_200_OK)

    @detail_route(methods=['post'], url_path='change-username')
    def change_username(self, request, *args, **kwargs):
        """
        修改用户的用户名
        """
        return Response(status=status.HTTP_200_OK, data={'msg': 'changed'})

# account/urls.py
router = routers.SimpleRouter()
router.register(r'', views.AccountViewSet, base_name='user')
urlpatterns = router.urls

以上代码,直接实现了对 account 的增(create)删(delete)改(update)和 account 列表(list),同时我又添加了set_passwordchange_username方法。这种形式没接触过的话很难理解,但是却很实用,尤其一个 model 有频繁的不同功能需求的时候,如果写SetPasswordViewChangeUsernameView这样类视图的实现,未免会很麻烦,这个时候 viewsets 就是很好的选择。

Serializers #

class TopicListSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.TopicModel
        fields = '__all__'
        read_only_fields = ('id', 'author_id')

Serializer 相当于 View 和 Model 的中间层,避免 view 频繁直接操作 model object 的同时,规范数据字段格式、内容、validator、提供序列化数据等等,同时可以定义方法实现对 model 的特定操作。serializer 不适合处理业务逻辑,它要做的就是对数据传入传出的处理,保证数据正确。通常为了验证和序列化各项数据,serializer 的代码加上 validator 也集中了较大的工作量。

其它 #

DRF 最主要的就是上面提到的一些概念,也许在刚接触 Django Web 的话没有接触过,但是这应该是 DRF 中最常用的。其他的比如 router、Throttling、validator 也比较实用,甚至也是必须掌握的,但是 DRF 的文档写的确实很详细,使用方法不再介绍。

总结 #

我的理解,DRF 是基于 “Don’t repeat yourself” 原则,提出一个Web开发的规范,它代码里提供了规范的接口,和最基础的使用例子,而在实际的复杂逻辑开发中,需要的是对各种概念的 “custom” 定制,几乎文档每一页都提供了定制的介绍。实际的应用里面不会是简单的增删改查,而是需要走一定的逻辑处理后的增删改查,同时还有对基础操作频繁的复用。DRF通过把web业务系统中会出现的每一步流程拆分,进行模块化,概念化,让开发者知道需要开发的业务属于哪一种概念,放到哪一个模块下面,从而在DRF的大框架下特定的位置编写,这样减少了一份功能多份代码实现的麻烦,也因为规范化 coding 提高了开发者的效率。

当然DRF也有缺点,它对新手不够友好,一些封装太极致,以至于不容易明白程序的执行流程,比如我初次看viewsetsGenericAPIView的时候。

最后我基于DRF的文档,学习过程中写了一个论坛demo:DRForum的论坛例子,可以直接runserver调试。通过127.0.0.1:8000/docs可以查看汇总的接口,这里使用了DRF Docs

本文共 2621 字,上次修改于 Jul 9, 2024,以 CC 署名-非商业性使用-禁止演绎 4.0 国际 协议进行许可。

相关文章

» Ubuntu 下部署 Django 应用

» 使用 Github Pages 建立免费的静态网站

» 这个博客