在前面的教程中我们介绍了什么是符合RESTful规范的API接口,并以博客为例使用了基于函数的视图(FBV)编写了文章资源的API,并测试了对文章的增删改查。在本篇文章中,我们将使用基于类的视图(Class-based View, CBV)重写之前的接口。如果你还没有阅读前两篇文章,建议先阅读。
为什么要使用基于类的视图(CBV)?
一个中大型的Web项目代码量通常是非常大的,如果全部使用函数视图写,那么代码的复用率是非常低的。而使用类视图呢,就可以有效的提高代码复用,因为类是可以被继承的,可以拓展的。特别是将一些可以共用的功能抽象成Mixin类或基类后可以减少重复造轮子的工作。
DRF推荐使用基于类的视图(CBV)来开发API, 并提供了4种开发CBV开发模式。
APIViewGenericAPIgenerics.*generics.ListCreateAPIViewViewSetModelViewSet很多人会疑惑,到底哪种CBV开发模式更好? 我们将逐一分析,并在文尾给出答案。本文代码较多,手机上如果竖屏阅读不方便,建议横屏阅读。
使用基础APIView类
DRF的APIView类继承了Django自带的View类, 一样可以按请求方法调用不同的处理函数,比如get方法处理GET请求,post方法处理POST请求。不过DRF的APIView要强大得多。它不仅支持更多请求方法,而且对Django的request对象进行了封装,可以使用request.data获取用户通过POST, PUT和PATCH方法发过来的数据,而且支持插拔式地配置认证、权限和限流类。
现在我们可以使用APIView类重写我们之前的函数视图了。
或许你已经注意到,这段代码跟之前基于函数的视图差别并不大。最大不同的是我们不需要在对用户的请求方法进行判断。该视图可以自动将不同请求转发到相应处理方法,逻辑上也更清晰。
as_view()如果不出意外,发送GET请求到v1/articles/你将看到同样的内容。
用Mixin类和GenericAPI类混配
ArticleSeralizerCategorySerializergenerics.GenericAPIgenerics.GenericAPI我们现在需要花些时间研究下这段代码,看看到底发生了什么。
GenericAPIView 类继承了APIView类,提供了基础的API视图。它对用户请求进行了转发,并对Django自带的request对象进行了封装。不过它比APIView类更强大,因为它还可以通过queryset和serializer_class属性指定需要序列化与反序列化的模型或queryset及所用到的序列化器类。
ListModelMixinCreateModelMixin.list() .create()RetrieveModelMixinUpdateModelMixinDestroyModelMixin或许你现在要问已经有get, post, delete等方法了,为什么mixin类引入的方法要以list, create, retrieve, destroy方法命名呢? 这是因为请求方法不如操作名字清晰,比如get方法同时对应了获取对象列表和单个对象两种操作,使用list和retrieve方法后则很容易区分。另外post方法接受用户发过来的请求数据后,有时只需转发不需要创建模式对象实例,所以post方法不能简单等于create方法。
serializer.save(author=request.user)perform_createperform_createCreateModelMixinperform_updateperform_destroy使用通用视图Generics.*类
将Mixin类和GenericAPI类混配,已经帮助我们减少了一些代码,但我们还可以做得更好,比如将get请求与mixin提供的list方法进行绑定感觉有些多余。幸好DRF还提供了一套常用的将 Mixin 类与 GenericAPI类已经组合好了的视图,开箱即用,可以进一步简化我们的代码,如下所示:
generics.ListCreateAPIViewgenerics.RetrieveUpdateDestroyAPIView寥寥几行,实现了我们所有想要的功能,神不神奇?
ListAPIViewRetrieveAPIViewRetrieveUpdateAPIViewquerysetserializer_class使用视图集ViewSet
ArticleListArticleDetailquerysetserializer_classquerysetseralizer_class如下所示:
使用视图集后,我们需要使用DRF提供的路由router来分发urls,因为一个视图集现在对应多个urls,而不像之前的一个url对应一个视图函数或一个视图类。
urls.pyReadOnlyModelViewSetDjango视图集viewset代码最少,但这是以牺牲了代码的可读性为代价的,因为它对代码进行了高度地抽象化。另外urls由router生成,不如自己手动配置的清楚。
小结
本文使用了DRF提供的多种基于类的API视图的重写了文章资源API。那么这几种方式到底哪种更好呢? 答案是各有利弊。小编个人认为大家只需掌握以下三种方式即可:
- 基础的API类:可读性最高、代码最多、灵活性最高。当你需要对的API行为进行个性化定制时,建议使用这种方式。
- 通用generics类:可读性好、代码适中、灵活性较高。当你需要对一个模型进行标准的增删查改全部或部分操作时建议使用这种方式。
- 使用视图集viewset: 可读性较低、代码最少、灵活性最低。当你需要对一个模型进行标准的增删查改的全部操作且不需定制API行为时建议使用这种方式。
至于mixin类和GenericAPI的混用,这个和generics类没什么区别,大家不看也罢。
get_querysetget_serializer_classlinks: