• 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类实现了六大接口,但是从实际开发角度分析有很多不合理的点:

    1. 没有群增,群整体改,群局部改,群删四个接口
    2. 删除只做字段的修改
    3. 响应的结果只有数据,没有数据状态码和状态信息

    所以针对以上问题,我们解决一下:

    路由层配置

    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')
    
  • 相关阅读:
    资源放送丨《Oracle存储过程中的性能瓶颈点》PPT&视频
    警示:一个update语句引起大量gc等待和业务卡顿
    周末直播丨细致入微
    Java VS Python:哪个未来发展更好?
    【LeetCode】279.完全平方数(四种方法,不怕不会!开拓思维)
    事件驱动
    Android初级教程以动画的形式弹出窗体
    Android简易实战教程--第五话《开发一键锁屏应用》
    迎战大数据-Oracle篇
    Android初级教程获取手机位置信息GPS与动态获取最佳方式
  • 原文地址:https://www.cnblogs.com/ghylpb/p/12154244.html
Copyright © 2020-2023  润新知