django rest framework中对于APIView、GenericAPIView、ModelViewSet、mixins扩展类的分析。python
根据实际程序来分析:数据库
urls.py
django
urlpatterns = [ re_path('users', UserAPIView.as_view()) ]
views.py
api
class UserAPIView(APIView): def get(self, request): users = User.objects.filter().all() ser = UserSerializer(instance=users, many=True) return CommonResponse(status=status.HTTP_200_OK, data=ser.data)
首先,UserAPIView
继承了APIView
,而APIView
继承了View
。iview
原生
View
里面定义了容许的http访问方式。函数![]()
urls.py
中,UserAPIView.as_view()
这条语句其实执行的是APIView
中的as_view()
方法,而APIView
中的as_view()
方法执行父类View
的as_view()
方法。源码分析
APIView中的as_view()url
View中的as_view()spa
其中关键的方法为dispatch
方法,根据UserAPIView-->APIView-->View
的继承顺序,该方法执行的是APIView
中的dispatch
方法。看下图。rest
dispatch
方法会获取请求方式,判断是不是http容许的请求方式,若是是的话,则分发执行UserAPIView
中对应的同名方法。
其实咱们也能够在UserAPIView
中自定义dispatch
方法,以下:
def dispatch(self, request, *args, **kwargs): func = getattr(self,request.method.lower()) return func(request,*args,**kwargs)
总结:CBV基于反射实现根据请求方式不一样,执行不一样的方法。
View:
as_view()
是入口,获得view函数view
函数,内部调用dispatch
函数完成请求分发dispatch
函数将请求方式映射为视图类的同名方法,获得相应结果APIView:
as_view()
是入口,经过执行父类中的as_view
方法获得view函数,而后在返回view
函数的时候免除csrf验证。view
函数,内部调用(APIView类中的)dispatch
方法完成请求分发。dispatch
方法中,将会二次封装request,完成三大验证(认证、受权、节流),再将请求方式映射为视图类的同名方法,完成请求的处理。首先看看GenericAPIView的源码。
能够看出,GenericAPIView
继承了APIView
。而后还有几个比较重要的类属性,稍后用到再讲。
再往下看它对外暴露的方法:
实际上是比较少的,并且大可能是get_xxx
之类的方法,也就是说,它的as_view()
、dispatch
等方法,其实都是按照APIView
的流程处理的。
首先咱们看get_queryset()
,它的意思是批量查找数据库的数据:
QuerySetDict类型以下:
接下来看get_object()
,它的意思是从数据库中获取单个对象:
filter_kwargs
封装查找条件,get_object_or_404
负责在queryset中查找符合条件的对象(object)。pk
,也就是说你的url
中必需要用pk
这个形参名进行分组捕获。不然就须要声明lookup_url_kwarg
,若是url中的参数为name
,那么lokup_url_kwarg="name"
,而后进行替换组建filter_kwargs
。固然若是你的查询条件不是用的pk
,就须要修改lookup_field
为字段名,如我不是按照pk
进行查询,而是按照name
,就修改lookup_field
为name
。check_object_permissions
主要的就是以上两个,get_serializer()
会调用get_serializer_class
获取序列化类,返回序列化类的执行结果。
class UserAPIView(GenericAPIView): queryset = User.objects serializer_class = UserSerializer def get(self, request): users = self.get_queryset() ser = self.get_serializer(instance=users, many=True) return CommonResponse(status=status.HTTP_200_OK, data=ser.data)
rest_framework.mixins
中,有五个扩展类,分别是:
ListModelMixin
:该类主要负责查询全部记录RetrieveModelMixin
:该类主要负责查询单条记录CreateModelMixin
:该类主要负责建立记录UpdateModelMixin
:该类主要负责对记录作更新操做DestroyModelMixin
:该类主要负责删除记录object
,是独立的子类。GenericAPIView
更高级的封装,配合GenericAPIView
使用有奇效。类 | 方法 | 描述 |
---|---|---|
ListModelMixin | list() | 查询全部,并返回Response对象 |
RetrieveModelMixin | retrieve() | 查询单条,并返回Response对象 |
CreateModelMixin | create() | 建立记录,并返回Response对象 |
UpdateModelMixin | update() | 更新记录,并返回Response对象 |
DestroyModelMixin | destroy() | 删除记录,并返回Response对象 |
class UserAPIView(GenericAPIView, ListModelMixin): queryset = User.objects serializer_class = UserSerializer def get(self, request): return CommonResponse(status=status.HTTP_200_OK, data=self.list(request).data)
若是继承全部的mixins类,则视图类就会写的很是冗长,可读性较差。
同时,每次都要在视图中return,那咱们会考虑能不能简化这步操做。
首先导入ModelViewSet
from rest_framework.viewsets import ModelViewSet
查看它的源码:
再看GenericViewSet
:
再看ViewSetMixin
:
注释的意思是说,它重写了as_view()
方法,绑定了http方法与视图函数中的方法,这让它必须接收一个actions
参数,actions
参数设置为一个字典。
urls.py
urlpatterns = [ re_path('users', UserAPIView.as_view(actions={"get": 'list'})) ]
views.py
class UserAPIView(ModelViewSet): queryset = User.objects serializer_class = UserSerializer
至于generics扩展类,提供了如下几个功能:
类 | 功能 |
---|---|
CreateAPIView | 建立记录 |
DestroyAPIView | 删除记录 |
UpdateAPIView | 更新单条记录 |
ListAPIView | 查询全部记录 |
RetrieveAPIView | 查询单条记录 |
ListCreateAPIView | 建立以及查询全部记录 |
RetrieveUpdateAPIView | 更新以及查询单条记录 |
RetrieveDestroyAPIView | 删除以及查询单条记录 |
RetrieveUpdateDestroyAPIView | 删除、更新、查询单条记录 |
因而可知,generics
中的扩展类都是结合了mixins扩展类
和GenericAPIView
,而且提供了标准的http方法同名方法,这些方法又都分别执行对应的mixins扩展类
中的方法。这和咱们mixins扩展类
部分中的示例
大致一致。
urls.py
urlpatterns = [ re_path('users', UserAPIView.as_view(actions={"get": 'list'})) ]
Views.py
class UserAPIView(ViewSetMixin, ListAPIView): queryset = User.objects serializer_class = UserSerializer
注:UserAPIView的as_view方法要接收actions参数,而咱们要使用的就是ViewSetMixin中重写的as_view方法,所以为了避免和ListAPIView中的as_view产生冲突,继承时要把ViewSetMixin放在前面。
看完上面的内容,下面再看这张图片,但愿能够帮助梳理本篇文章的内容: