这边先分享一个DRF的比较全的各种组件的用法的地址(DRF | YUAN),但是讲得比较专业,我这边更通俗,是按照封装过程来讲解的,所以可能会更清晰一步一步怎么来的,为什么
DRF视图!!!!!
DRF提供的视图的主要作用:
from django.views import View
就是从FBV到CBV的第一步,面向资源编程,将对一个资源的操作全放到一个类中,需要继承View类,是所有基于类的view的父类,它负责将视图连接到URL、HTTP 方法调度,需要在url模式中改成cccView.as_view() ,as_view()返回view函数,如图:
from rest_framework.view import APIView
它是继承自Django的View的,
重写了as_view()方法和dispatch()方法的分发逻辑,还小干了一些事情,执行了父类的as_view()方法,最后也是返回view函数
最不一样的就是dispatch():构建了新的request对象,初始化:认证、限流、限流组件
Django,View中的request是request.POST和request.GET,且不会解析JSON数据,因为其针对的是urlencoded的数据
APIView中新构建的request对象,是request.data和request.query_param,所以拿请求体的数据时会异常方便!(request._request会取到Django原生的request)如图:
from rest_framework.generics import GenericAPIView
GenericAPIView是继承自APIView,主要曾加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。通常在使用时,可搭配一个多个Mixin扩展类
为什么要GenericAPIView??因为我们发现在不断重复增删改查查的逻辑中,查询出来的数据和序列化器都是一直不变的,那么就可以直接抽象出来(是关键字,一个字都不能错),以后想对哪个表做基本的五个接口,只需要改动queryset和serializer_class即可
提供的关于序列化器使用的属性与方法,这四个方法主要是调度用的(在GenericAPIView的源码中被定义)
1. get_serializer_class(self)
获取序列化器类(前提是已经在view视图下放好了全局变量serializer_class)
调用方法:self.get_serializer_class()
2. get_serializer(self,args, *kwargs)
获取序列化器对象 (前提是已经在view视图下放好了全局变量serializer_class),这个内部会调用获取序列化器类,并实例化后返回对象
3. get_queryset(self)
获取查询集结果(前提是已经在view视图下放好了全局变量queryset)
调用方法: self.get_queryset()
4.get_object(self)
获取单一资源对象(主要是使用在查取单个对象的接口中),它内部会找出queryset,并且不又名参接受位置参数的传参,而是只接受有名分组的传参,必须叫pk,它才会按照关键字传参
现在基于上面的这部分知识来完成这View接口
1.查所有get接口(View中)
2.增加post接口(View中)
同理
3.查单个get接口(DetailView中)
注意参数是ok和get_object()会直接拿到模型类对象
4.更新单个put接口(DetailView中)
同理:
5.删除单个delete接口(DetailView中)
from rest_framework.mixins import ListModeMixin,CreateModelMixin,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin
用GenericAPIView时,五个接口内部的逻辑完全可以封装起来,然后直接调用,而不是一个表就再粘贴一遍,DRF给封装好了
1.ListModeMixin
ListModeMixin中有list方法,就是增加了一个过滤和分页,然后就取所有,并做序列化返回serializer.data,和之前的逻辑一模一样,如图:
所以写接口的时候就可以通过GenericAPIView和ListModelMixin配合着直接来:
2.CreateModelMixin
CreateModelMixin中封装了create方法,就是post,还有perform_create方法,只有.save()
所以也直接来:
RetrieveModelMixin中是retrieve方法
4.UpdateModelMixin
UpdateModelMixin中是update方法,直接来
DestroyModelMixin中是destroy方法,并返回一个空的对象,如图:
直接来看到最终的DetailView:
实现了上面的接口后,发先虽然五个接口内部都只剩一句话了,但是还是需要对每一个表写五次,所以进行再封装,
View的封装在一块(get,post,ListModeMixin,CreateModelMixin,GenericAPIView是一个整体),变成ListCreateAPIView
DetailView的封装在一块(get,put,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin,GenericAPIView是一个整体)
1. from rest_framework.generics import ListCreateAPIView
如图ListCreateAPIView的源码:
所以只需要这样实现接口,不需要写这些方法了 :
2.from rest_framework.generics import RetrieveUpdateDestroyAPIView
同理DetailView中的接口只需这样实现:
基于对上面的理解,之所以一个资源需要用两个View类,是因为分发逻辑时根据请求方式,所以两个类中用的是一套变量,即序列化器和query_set;所以想要将五个方法写在一个类中,必然需要改变分发机制
看一下ViewSet的源码,发现它没有继承GenericView,所以Mixins混合类都不能用
因为ViewSet这个类继承了ViewSetMixin,那么路由的as_view()中就可以加参数了,参数是一个字典,制定了请求方式到视图类中方法的映射,例如请求方式为get且含数字的对于的方法名是get_object; 如图所示:
那么ViewSetMixin干了什么来改变分发逻辑的呢??,最重要的就是下面的图中展示的,将字典(每个action就是一个字典)按照键值对取出,并将value用getattr()将value反射成函数变量,然后再给method设置成这个函数变量,所以当取到get字符串的时候就直接拿到自己写的这个函数变量了(相当于我拦截你一下,让你别找get了,找我让你找到):
from rest_framework.viewsets import ViewSet
有了映射就可以跟着映射给函数取名了
基于GenericViewSet的最终版本
先看GenericViewSet的源码
发现是继承了ViewSetMixin(完成了路由的重新分发),和GenericAPIView(调度的各种方法);是这两种的组合,所以此时有了generic的调度方法就可以使用ListModeMixin,CreateModelMixin,UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin了,这五个类中的五个方法:list、create、update、retrieve、destroy;;那是不是就能充当路由的字典当中的value值了;
本质逻辑就是将:ViewSetMixin的函数对应功能和GenericAPIView给结合起来了,使得可以用这五个混合类
所以最后就可以这样写:
ModelViewSet什么都没干,就将上面的几个类都继承了
最后的这个确实好用但是,会失去灵活性,不能自定义逻辑,所以有时候需要将比如ListModeMixin拿过来之后,自己在View类中重写list函数来覆盖掉ListModeMixin中的list
注册路由!!!!
原来的路由看上去就很复杂,很不好用不好写,所以去使用注册路由就很方便
1.生成个默认的路由对象
第一个参数就是url最后的那个资源,不用斜杠;每注册一个都会生成一个之前写的path和一个re_path
在列表外面再加上:即可