从「接口测试平台」专栏《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工作原理等等,➕关注啊胡,后续持续更新哦~

好文链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: