Django rest-framework视图家族
目录
视图家族
- 视图类 GenericAPIView:包含两大视图类(APIView、GenericAPIView)
- 视图工具类 mixins:包含五大工具类,六大工具方法
- 工具视图类 generics:包含九大工具视图类,一堆mixins工具类与GenericAPIView视图基类组合
- 视图集 viewsets:可自定义映射关系
from rest_framework import views,generics,mixins,viewsets
视图类 GenericAPIView
两大视图类:APIView、GenericAPIView
APIView
from rest_framework.views import APIView
- 继承基本View,拥有View所有的功能
- 重写了as_view()方法,禁用了csrf认证
- 重写dispatch,封装请求、响应、渲染、异常、解析、三大认证模块
- 封装一堆属性,可完成视图类的局部配置
GenericAPIView
from rest_framework.generics import GenericAPIView
- 继承APIView,拥有APIView所有的功能
- 提供
get_queryset
方法:配置queryset
类属性,群查获取QuerySet对象 - 提供
get_object
方法:配置lookup_url_kwarg
类属性,单查获取单个对象 - 提供
get_serializer
方法:配置serializer_class
类属性,提供序列化类并使用自定义的序列化类序列化
总结:GenericAPIView就是在APIView基础上额外提供了三个方法和三个类属性,如果不配合视图工具类,则体现不出来优势所在
使用它的好处:视图中的增删改查逻辑其实大差不差,但操作的资源不一致(操作的资源指的是models模型类和序列化类),将资源形成配置,操作逻辑一致,就可以完成封装
使用GenericAPIView类
- 继承GenericAPIView类
- 配置对哪个表进行操作
- 配置使用哪个序列化类
群查
from rest_framework.generics import GenericAPIView class ViewGenericAPIView(GenericAPIView): # 配置关联表的属性 # 如果只写models.Car.objects的话那就是manager对象,不是QuerySet对象 queryset = models.Car.objects.filter(is_delete=False).all() # 配置使用的序列化类 serializer_class = serializer.CarModelSerializer # 群查 def get(self,request,*args,**kwargs): # 帮我们去表里面拿数据 car_query = self.get_queryset() # 帮我们去序列化 car_ser = self.get_serializer(car_query,many=True) return APIResponse(results=car_ser.data)
单查
from rest_framework.generics import GenericAPIView class ViewGenericAPIView(GenericAPIView): # 配置关联表的属性 # 如果只写models.Car.objects的话那就是manager对象,不是QuerySet对象 queryset = models.Car.objects.filter(is_delete=False).all() # 配置使用的序列化类 serializer_class = serializer.CarModelSerializer # 配置查询的条件为pk,单查走pk过滤的条件 lookup_url_kwarg = 'pk' # 单查 def get(self,request,*args,**kwargs): car_obj = self.get_object() car_ser = self.get_serializer(car_obj) return APIResponse(results=car_ser.data)
视图工具类 mixins
- 在GenericAPIView的基础上提供了五个类,六个方法六大接口(单查、群查、单增、单整体改、单局部改、单删)
- 使用的时候需要配合继承GenericAPIView类
五大工具类
- RetrieveModelMixin:单查类
- ListModelMixin:群查类
- CreateModelMixin:单增类
- UpdateModelMixin:单整体和单局部改类
- DestroyModelMixin:单删类
六大工具方法
- retrieve:单查方法
- list:群查方法
- create:单增方法
- update:单整体改方法
- partial_update:单局部改方法
- destroy:单删方法
使用mixins的六大工具方法
- 继承GenericAPIView类
- 配置对哪个表进行操作
- 配置使用哪个序列化类
from rest_framework import mixins class ViewMixinsAPIView(mixins.RetrieveModelMixin, mixins.ListModelMixin, mixins.CreateModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, GenericAPIView): queryset = models.Car.objects.filter(is_delete=False).all() serializer_class = serializer.CarModelSerializer lookup_url_kwarg = 'pk' # 单查群查 def get(self, request, *args, **kwargs): pk = kwargs.get('pk') if pk: return self.retrieve(request, *args, **kwargs) return self.list(request, *args, **kwargs) # 单增 def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) # 单整体改 def put(self,request, *args, **kwargs): return self.update(request, *args, **kwargs) # 单局部改 def patch(self,request, *args, **kwargs): return self.partial_update(request, *args, **kwargs) # 单删 def delete(self,request, *args, **kwargs): # django中的删除是真正的删除 # 删除接口一般是自己实现重写到的,因为真正的业务不需要真正的删除 pass # django源代码中是真的删除 return self.destroy(request, *args, **kwargs)
工具视图类 generics
工具类加视图类的组合,只要继承工具该类,就有响应的方法,
- 帮我们将不同的mixins工具类与GenericAPIView视图类进行组合,我们不再需要继承GenericAPIView类
- 不同的组合封装成一个个的类,实现对应的请求方法(get、post、put、patch、delete)
随后就是用单查就继承单查的接口,用群查就继承群查的接口即可。
使用generics的工具类实现接口
- 配置对哪个表进行操作
- 配置使用哪个序列化类
from rest_framework import generics class ViewGenericsAPIView(generics.RetrieveAPIView, generics.ListAPIView, generics.CreateAPIView, generics.UpdateAPIView, generics.DestroyAPIView): # 单查和群查只能使用一个get,具体调用哪个要看继承的顺序 queryset = models.Car.objects.filter(is_delete=False).all() serializer_class = serializer.CarModelSerializer lookup_url_kwarg = 'pk' # 有删除需求的接口继承DestroyAPIView,重写destroy完成字段的删除 def destroy(self, request, *args, **kwargs): pass
视图集 viewsets
- 重写as_view方法,增加action参数(可以完成路由层的请求方法映射关系)
- 可以在路由层中自定义请求方法的映射关系
使用viewsets的视图集类实现接口
- 配置对哪个表进行操作
- 配置使用哪个序列化类
可自定义路由层中请求方法的映射关系来实现接口
路由层
url(r'^v5/cars/$', views.ViewViewsetsAPIView.as_view({ "get":"list", "post":"create" })), url(r'^v5/cars/(?P<pk>\d+)/$', views.ViewViewsetsAPIView.as_view({ "get":"retrieve", "put":"update", "patch":"partial_update", "delete":"destroy" })),
视图层
from rest_framework import viewsets # 视图集类 class ViewViewsetsAPIView(viewsets.ModelViewSet): queryset = models.Car.objects.filter(is_delete=False).all() serializer_class = serializer.CarModelSerializer lookup_url_kwarg = 'pk'
完善viewsets的视图集类实现接口
以上的步骤我们继承视图集的ModelViewSet类实现了六大接口,但是从实际开发角度分析有很多不合理的点:
- 没有群增,群整体改,群局部改,群删四个接口
- 删除只做字段的修改
- 响应的结果只有数据,没有数据状态码和状态信息
所以针对以上问题,我们解决一下:
路由层配置
url(r'^v5/cars/$', views.ViewViewsetsAPIView.as_view({ "get":"list", "post":"create", "put":"many_update", "patch":"many_partial_update", "delete":"many_destroy" })), url(r'^v5/cars/(?P<pk>\d+)/$', views.ViewViewsetsAPIView.as_view({ "get":"retrieve", "put":"update", "patch":"partial_update", "delete":"destroy" })),
实现群增,群整体改,群局部改,群删四个接口
视图层配置
# 群整体改 def many_update(self,request,*args,**kwargs): try: pks = [] for dic in request.data: pks.append(dic.pop('pk')) car_query = models.Car.objects.filter(is_delete=False, pk__in=pks).all() if len(pks) != len(car_query): raise Exception('pk对象不存在') except Exception as e: return Response({'detail': '%s' % e}, status=400) car_ser = self.get_serializer(instance=car_query,data=request.data,many=True) car_ser.is_valid(raise_exception=True) car_obj = car_ser.save() return APIResponse(results=self.get_serializer(car_obj,many=True).data) # 群局部改 def many_partial_update(self,request,*args,**kwargs): try: pks = [] for dic in request.data: pks.append(dic.pop('pk')) car_query = models.Car.objects.filter(is_delete=False, pk__in=pks).all() if len(pks) != len(car_query): raise Exception('pk对象不存在') except Exception as e: return Response({'detail': '%s' % e}, status=400) car_ser = self.get_serializer(instance=car_query,data=request.data,many=True,partial=True) car_ser.is_valid(raise_exception=True) car_obj = car_ser.save() return APIResponse(results=self.get_serializer(car_obj,many=True).data) # 群删 def many_destroy(self,request,*args,**kwargs): pks = request.data try: rows = models.Car.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True) except: return APIResponse(1, '数据有误') if rows: return APIResponse(msg='删除成功') return APIResponse(1, '删除失败') # 群增和单增必须使用同一个接口,都要走create方法,重写create方法,使用逻辑拆分 def create(self, request, *args, **kwargs): if isinstance(request.data,list): car_ser = self.get_serializer(data=request.data,many=True) car_ser.is_valid(raise_exception=True) car_obj = car_ser.save() return APIResponse(msg="群增成功",results=self.get_serializer(car_obj,many=True).data) return super().create(request, *args, **kwargs)
实现删除只做字段的修改
# 解决2 destroy方法完成对字段的修改 def destroy(self, request, *args, **kwargs): car_obj = self.get_object() if not car_obj: return APIResponse(1,msg="删除失败") car_obj.is_delete = True car_obj.save() return APIResponse(msg="删除成功")
实现返回信息包含数据状态码和状态信息
# 解决3 群查有状态码和状态信息,重写list方法 def list(self, request, *args, **kwargs): response = super().list(request, *args, **kwargs) return APIResponse(results=response.data) # 重写retrieve方法,单查有状态码和状态信息 def retrieve(self, request, *args, **kwargs): response = super().retrieve(request, *args, **kwargs) return APIResponse(results=response.data)
路由组件(了解)
使用SimpleRouter结合视图组件进行路由配置
from django.conf.urls import url,include from rest_framework.routers import SimpleRouter router = SimpleRouter() # 将所有路由与ViewSet视图类的都可以注册,会产生'^v5/cars/$' 和 '^v5/cars/(?P<pk>[^/.]+)/$'的url router.register('v5/cars',views.ViewViewsetsAPIView,basename='car') urlpatterns = [ # 第一种添加子列表方式 url(r'^', include(router.urls)), ] # 第二种添加子列表方式 # urlpatterns.extend(router.urls)
路由组件源码部分
如果想实现其他映射关系的话,修改源码就行了
routes = [ # List route. # 群增,如果想要在url中奖请求方式映射关系改变的话,可以重写这个 Route( url=r'^{prefix}{trailing_slash}$', mapping={ 'get': 'list', 'post': 'create' }, name='{basename}-list', detail=False, initkwargs={'suffix': 'List'} ), # Dynamically generated list routes. Generated using # @action(detail=False) decorator on methods of the viewset. DynamicRoute( url=r'^{prefix}/{url_path}{trailing_slash}$', name='{basename}-{url_name}', detail=False, initkwargs={} ), # Detail route. Route( url=r'^{prefix}/{lookup}{trailing_slash}$', mapping={ 'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy' }, name='{basename}-detail', detail=True, initkwargs={'suffix': 'Instance'} ), # Dynamically generated detail routes. Generated using # @action(detail=True) decorator on methods of the viewset. DynamicRoute( url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$', name='{basename}-{url_name}', detail=True, initkwargs={} ), ]
或者自定义路由对象
from rest_framework.routers import Route, DynamicRoute, SimpleRouter as DRFSimpleRouter class SimpleRouter(DRFSimpleRouter): routes = [ # List route. /资源s/ Route( url=r'^{prefix}{trailing_slash}$', mapping={ 'get': 'list', # 群查 'post': 'create', # 单增、群增 'put': 'many_update', # 群整改 'patch': 'many_partial_update', # 群局改 'delete': 'many_destroy', # 群删 }, name='{basename}-list', detail=False, initkwargs={'suffix': 'List'} ), # Dynamically generated list routes. Generated using # @action(detail=False) decorator on methods of the viewset. DynamicRoute( url=r'^{prefix}/{url_path}{trailing_slash}$', name='{basename}-{url_name}', detail=False, initkwargs={} ), # Detail route. /资源s/(pk)/ Route( url=r'^{prefix}/{lookup}{trailing_slash}$', mapping={ 'get': 'retrieve', # 单查 'put': 'update', # 单整改 'patch': 'partial_update', # 单局改 'delete': 'destroy' # 单删 }, name='{basename}-detail', detail=True, initkwargs={'suffix': 'Instance'} ), # Dynamically generated detail routes. Generated using # @action(detail=True) decorator on methods of the viewset. DynamicRoute( url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$', name='{basename}-{url_name}', detail=True, initkwargs={} ), ] # 对外提供router对象 router = SimpleRouter() # eg: router.register('users', UserModelViewSet, basename='user')