django rest framework中对于APIView、GenericAPIView 、ModelViewSet 、mixins扩展类的分析 。
APIView
示例
根据实际程序来分析:
urls.py
urlpatterns = [ re_path('users', UserAPIView.as_view()) ]
views.py
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。
原生
View里面定义了允许的http访问方式。
urls.py中,
UserAPIView.as_view()这条语句其实执行的是
APIView中的
as_view()方法,而
APIView中的
as_view()方法执行父类
View的
as_view()方法。
APIView中的as_view()
View中的as_view()
其中关键的方法为
dispatch方法 ,根据
UserAPIView-->APIView-->View的继承顺序,该方法执行的是
APIView中的
dispatch方法 。看下图。
dispatch方法会获取请求方式,判断是否是http允许的请求方式 ,如果是的话,则分发执行
UserAPIView中对应的同名方法。
其实我们也可以在
UserAPIView中自定义
dispatch方法,如下:
def dispatch(self, request, *args, **kwargs): func = getattr(self,request.method.lower()) return func(request,*args,**kwargs)
总结:CBV基于反射实现根据请求方式不同 ,执行不同的方法 。
View与APIView的执行流程
View:
-
as_view()
是入口,得到view函数 - 请求来了调用
view
函数,内部调用dispatch
函数完成请求分发 -
dispatch
函数将请求方式映射为视图类的同名方法 ,得到相应结果
APIView:
-
as_view()
是入口,通过执行父类中的as_view
方法得到view函数,然后在返回view
函数的时候免除csrf验证。 - 请求来了调用
view
函数 ,内部调用(APIView类中的)dispatch
方法完成请求分发。 - 在
dispatch
方法中 ,将会二次封装request,完成三大验证(认证、授权、节流),再将请求方式映射为视图类的同名方法 ,完成请求的处理 。
GenericAPIView
执行流程
首先看看GenericAPIView的源码。
可以看出,
GenericAPIView继承了
APIView。然后还有几个比较重要的类属性,稍后用到再讲 。
再往下看它对外暴露的方法:
其实是比较少的 ,而且大多是
get_xxx之类的方法,也就是说,它的
as_view()、
dispatch等方法 ,其实都是按照
APIView的流程处理的。
重要方法
首先我们看
get_queryset(),它的意思是批量查找数据库的数据:
- 可以看到,queryset属性必须进行赋值 ,按照继承顺序,我们直接在自己定义的视图类中声明queryset属性并进行赋值。
- 在if判断中,如果queryset是一个QuerySet对象 ,就获取全部 ,得到一个QuerySetDict对象,否则原样返回 。
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)
mixins扩展类
- 在
rest_framework.mixins
中 ,有五个扩展类 ,分别是:-
ListModelMixin
:该类主要负责查询所有记录 -
RetrieveModelMixin
:该类主要负责查询单条记录 -
CreateModelMixin
:该类主要负责创建记录 -
UpdateModelMixin
:该类主要负责对记录做更新操作 -
DestroyModelMixin
:该类主要负责删除记录
-
- 这五个类都继承于
object
,是独立的子类 。 - 针对
GenericAPIView
更高级的封装,配合GenericAPIView
使用有奇效。 - 会自动进行
return Response()
,所以就不用我们再对返回对象进行包装了。
类 | 方法 | 描述 |
---|---|---|
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)
ModelViewSet
如果继承所有的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扩展类
至于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放在前面。
看完上面的内容 ,下面再看这张图片,希望可以帮助梳理本篇文章的内容: