
啊胡
2022/09/26阅读:71主题:绿意
Django如何快速开发RESTful风格的接口?
从「接口测试平台」专栏《08 接口测试平台 项目管理模块开发(一)》开始进行接口开发,可以看到使用Django REST Framework(下面简称DRF
)只需要简单的几行代码就完成了6个restful风格接口的开发,这篇文章就带大家搞清楚DRF是如何做到的?文章有点长,含部分源码解析,可以收藏后再看哦~
截图为下面👇代码实现的6个接口。
class ProjectsViewSet(ModelViewSet):
queryset = Projects.objects.filter()
serializer_class = ProjectsSerializer

不使用DRF如何开发接口?
首先来看下,如果不使用DRF,仅使用Django,完成这六个接口,一般情况需要6个方法来完成,然后根据不同的功能需要编写不同的逻辑。篇幅有限,方法中的具体业务逻辑就不一一实现了。
def list(request):
pass
def create(request):
pass
def detail(request,project_id):
pass
def delete(request,project_id):
pass
def update(request,project_id):
pass
def update_partial(request,project_id):
pass
上面分成6个方法的写法,需要定义6个不同的路由,分别指向不同的方法,如果要优化,可以根据RESTful风格接口的接口定义,可以只提供一个方法,根据请求方式不同进行判断,这样只需要定义两个路由,一个路由的路径不带参数,一个路径带参数。
def project(request,project_id):
if request.method == "GET":
pass
if request.method == "PUT":
pass
if request.method == "PATCH":
pass
if request.method == "DELETE":
pass
def projects(request):
if request.method == "GET":
pass
if request.method == "POST":
pass
后面Django又给出了class-based-views的方式,即面向对象编写View,代码优化如下:
class ProjectsView(View):
def get(self, request):
pass
def post(self, request):
pass
class ProjectView(View):
def get(self, request):
pass
def put(self, request):
pass
def patch(self, request):
pass
def delete(self, request):
pass
针对不同的使用场景,Django还提供了更多方便的View类,比如DetailView
、ListView
、CreateView
、UpdateView
、DeleteView
。如果要完成对应的功能可以继承相应的类。
使用DRF如何开发接口?
针对普通的增删改查,DRF提供了更便利的方式来创建接口。同样是上面6个接口,DRF的代码更简洁:
#View层定义
class ProjectsViewSet(ModelViewSet):
queryset = Projects.objects.filter()
serializer_class = ProjectsSerializer
#路由定义
router = DefaultRouter()
router.register(r'projects', ProjectsViewSet, basename="projects")
urlpatterns = [
re_path(r'^', include(router.urls)),
]
上述不超过10行代码就可以完成上面6个接口的完整逻辑,下面给大家分别介绍DRF是如何做到的?
ModelViewSet
通过源代码可以看到这个类其实没做什么,只是继承了多个mixins和GenericViewSet,下面将分别介绍这些类。

GenericViewSet
它继承了ViewSetMixin
和GenericAPIView
。
-
ViewSetMixin
主要是重写了as_view
方法,提供了action来判断不同请求,这个action在后面完成不同请求的业务逻辑存在很大的用处,比如:列表和详情页都是GET请求,如果要使用不同的serializer,因为都是get请求无法通过请求方式判断,在这里可以通过action来进行判断,列表的action是list,而详情的action是retrieve。 -
GenericAPIView
继承了APIView
,而APIView
继承了Django提供的View
类。APIView
在View
类的基础上提供了更多的特性,比如authentication_classes
用来进行权限控制等等,后面一一给大家介绍。而GenericAPIView
在APIView
基础上增加了更多的特性,使得DRF可以快速开发出上诉6个通用类型的接口。如果是一些特殊接口逻辑则可以继承APIView
。
ListModelMixin和GenericAPIView
首先分析下,如果要完成一个列表接口,业务逻辑是怎样的?以Django原生的方法来举例:
def list(request):
# 从数据库中查询列表
# 将数据库查询的数据转换为前端要展示的数据
# 返回给前端
pass
这些业务逻辑,DRF的ListModelMixin
已经帮你完成了,如下图为DRF的源码。
-
首先执行 self.get_queryset()
方法,这个方法的逻辑在GenericAPIView
中实现,实际上就是获取定义的queryset
字段。

-
然后是 self.filter_queryset
执行过滤操作,过滤逻辑需要由具体的过滤的方法来实现 -
接下来是 self.paginate_query
判断是否需要进行分页操作,分页逻辑需要由具体的分页的方法来实现 -
然后使用 self.get_serializer
方法,这个方法的逻辑在GenericAPIView
中实现,实际上就是获取定义的serializer_class
字段。

-
最后就是将数填充到Response中返回给前端, serializer.data
主要内容就是将数据库查询的数据转换为可以返回给前端的数据格式,即DTO转换为VO,当然DRF的serializer有更强大功能,后面给大家介绍。
如上所述,因此在业务逻辑中只需要继承ListModelMixin
然后提供queryset
(要查询的数据)和serializer
(数据结构解析),剩下的事情全部由ListModelMixin
实现了,如果不需要暂时分页和查询功能,下面4行就足以完成一个列表接口。
class ProjectsViewSet(mixins.ListModelMixin,
viewsets.GenericViewSet):
queryset = Projects.objects.filter()
serializer_class = ProjectsSerializer
CreateModelMixin和GenericAPIView
同样来分析下,如果要完成一个创建接口,业务逻辑是怎样的?同样以Django原生的方法来举例:
def create(request):
# 从请求中获取到数据,验证数据格式是否正确
# 将数据转化为数据库存储格式的数据
# 将数据保存到数据库
# 将结果返回给前端
pass
同样这些业务逻辑,DRF的CreateModelMixin
也已经完成了,如下图为DRF的源码。

-
request.data
从请求体中获取到请求数据,然后使用serializer进行数据转化,将原始请求的数据转换为对象 -
is_valid
校验字段是否合法(这里说到了serializer的另一个功能数据字段校验) -
serializer.save()
将数据转化为数据库存储格式并保存 -
保存成功,封装响应返回到前端。
如上所诉,如果没有额外的需求,在业务逻辑中只需要继承CreateModelMixin
就可以提供一个带有完整业务逻辑的新增接口了。
class ProjectsViewSet(mixins.CreateModelMixin,
viewsets.GenericViewSet):
queryset = Projects.objects.filter()
serializer_class = ProjectsSerializer
DestroyModelMixin和GenericAPIView
同样来分析下,如果要完成一个删除接口,业务逻辑是怎样的?同样以Django原生的方法来举例:
def create(request):
# 从请求中获取到要删除对象的ID(或者其他数据)
# 跟据ID在数据库中进行删除操作
# 将数据保存到数据库
# 将结果返回给前端
pass
同样这些业务逻辑,DRF的DestroyModelMixin
也已经完成了,如下图为DRF的源码。

-
首先获取到删除的对象 self.get_object
,这个方法也是在GenericAPIView
实现的,lookup_field
字段默认是"pk"
就是主键ID,如果要现实根据用户名查找获取其他方式,可以将lookup_field
字段改为对应的字段名称。

-
查询到指定的对象就可以删除了,删除成功后返回响应
如上所诉,如果没有额外的需求,在业务逻辑中只需要继承DestroyModelMixin
就可以提供一个带有完整业务逻辑的删除接口了。
class ProjectsViewSet(mixins.CreateModelMixin,
viewsets.GenericViewSet):
queryset = Projects.objects.filter()
RetrieveModelMixin和GenericAPIView
同样来分析下,如果要完成一个详情接口,业务逻辑是怎样的?同样以Django原生的方法来举例:
def detail(request):
# 从请求中获取到查询对象的ID(或者其他数据)
# 跟据ID在数据库中进行查询操作
# 将数据库查询的数据转换为前端要展示的数据
# 将结果返回给前端
pass
同样这些业务逻辑,DRF的RetrieveModelMixin
也已经完成了,如下图为DRF的源码。

-
使用 self.get_object
获取到查询对象,这个已经在上面介绍过,不再进行介绍 -
使用serializer将数据转换为想要返回的数据结构然后返回
如上所诉,如果没有额外的需求,在业务逻辑中只需要继承RetrieveModelMixin
就可以提供一个带有完整业务逻辑的详情接口了。
class ProjectsViewSet(mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
queryset = Projects.objects.filter()
UpdateModelMixin和GenericAPIView
同样来分析下,如果要完成一个更新接口,业务逻辑是怎样的?同样以Django原生的方法来举例:
def update(request):
# 从请求中获取到查询对象的ID(或者其他数据)
# 跟据ID在数据库中进行查询操作
# 将数据进行更新后保存
# 将结果返回给前端
pass
同样这些业务逻辑,DRF的UpdateModelMixin
也已经完成了,如下图为DRF的源码。

-
首先判断是否是部分更新请求,即PATCH请求,全部更新则是PUT请求。 -
同样使用 self.get_object
获取到要更新的对象,这个已经在上面介绍过,不再进行介绍 -
然后使用serializer将请求体数据 request.data
转换为对象并进行校验,部分更新请求则只校验更新的字段,全部更新则是要校验所有的字段。 -
最后进行数据更新,更新后构造响应返回给前端
如上所诉,如果没有额外的需求,在业务逻辑中只需要继承UpdateModelMixin
就可以提供两个带有完整业务逻辑的更新接口了。
class ProjectsViewSet(mixins.UpdateModelMixin,
viewsets.GenericViewSet):
queryset = Projects.objects.filter()
DRF快速开发RESTful风格接口的介绍就到这里啦,当然其中还涉及到很多知识点,比如列表查询
、列表分页
、Serializer工作原理
等等,➕关注啊胡,后续持续更新哦~
作者介绍
