zoukankan      html  css  js  c++  java
  • DRF视图家族

    一、DRF的视图家族

    1. DRF中视图家族成员和配套路由

    (1)视图类:views

    • # 包括:APIView,GenericAPIView(generics中)两个视图类
      
      APIView类:
      	1. 继承View,所以拥有View的所有
          2. 重写as_view方法
          3. 重写dispatch方法
          4. 一系列类属性
          
      GenericAPIView类:
      	1. 继承APIView,所以拥有APIView的所有
          2. 有get_queryset方法,来配置queryset类属性,提供了要处理的资源对象列表
          3. 在第二条基础上,通过get_object方法,提供了处理的具体资源对象。配置lookup_url_kwarg类属性是因为配置的url中传的有名分组的名字可能不是pk,这使就可以通过lookup_url_kwarg来替代pk
          4. get_serializer方法,配置serializer_class类属性,提供视图类相关的序列化对象
          
          ***配置的queryset属性和serializer_class属性为视图工具准备数据***
          (GenericAPIView类中:
          queryset = None
          serializer_class = None
          lookup_field = 'pk'
          lookup_url_kwarg = None
          )
          
          
          
      总结:GenericAPIView就是在APIView基础上额外提供了三个方法(get_queryset,get_object,get_serializer),三个类属性(queryset,lookup_url_kwarg,serializer_class),如果不配合视图工具类,体现不出优势。
      
      GenericAPIView内部调用视图工具中的6种操作资源的对应实现体方法
      
      目的:视图中的增删改查逻辑相似,但操作的资源不一致。操作资源就是操作:资源对象、资源对象们 以及 资源相关的序列化类,将这三者形成类属性的配置,由程序员自己从外部根据不同资源传入不同类属性,这样一来,不同资源的操作逻辑就都一致了,就可以进行封装
      
      

    (2)视图工具类:mixins

    • 视图工具类的作用就是完成对资源的操作,并返回数据的序列化结果给前端

    • 视图工具含有的6种类的实现体方法,调用这6个实现体方法的方法就是GenericAPIView提供的,所以要配合GenericAPIView类使用

    • # 包括:
      CreateModelMixin  # 单增工具 
      	含有create方法,实现增逻辑和返回响应结果
      RetrieveModelMixin  # 单查工具
      	含有retrieve方法,实现单查逻辑和返回响应结果
      ListModelMixin  # 群查工具
      	含有list方法,实现群查逻辑和返回响应结果
      UpdateModelMixin  # 单改工具(包含整体单改和局部单改)
      	含有update方法和partial_update方法,分别实现整体单增和局部单增逻辑和返回响应结果
          (update方法内部的含有partial参数,默认是False。partial_update方法其实就是将partial参数置为True,再用update方法)
      DestroyModelMixin  # 单删工具
      	含有destroy方法,实现单删逻辑和返回响应结果
      

    (3)工具视图类:generics

    • 工具视图类是对视图工具和视图类两者的继承,内部包含了对资源的操作和处理后的响应

    • # 包括:以下等共九种工具视图(其实就是对视图工具和视图的组合)
      
      CreateAPIView  # 单增工具视图 
      RetrieveAPIView  # 单查工具视图
      ListAPIView  # 群查工具视图
      RetrieveUpdateDestroyAPIView  # 单查单改(包括整体改和局部改)单删工具视图
      

    (4)视图集:viewsets

    • 实现:在一个请求方式只有一条url路由对应时,对应的单操作和群操作可以区分开。

    • 视图集的核心是继承了ViewSetMixin

    • 视图集中的

      • 1. ViewSetMixin类
        ViewSetMixin类重写了as_view方法,相比APIView的as_view方法,额外多出了一个参数actions
        
        重写的as_view方法可以传字典形式的参数。通过传入的参数将不同的请求方式对应到各自的处理函数
        例子:as_view({'get': 'list'}) 传入的{'get': 'list'}就被actions接收,原理是将get请求映射给视图类的list函数进行处理
        
        2. 两个视图集基类:GenericViewSet和ViewSet的区别
        
        都继承了ViewSetMixin类,只是继承的视图类不同    
        
        GenericViewSet(ViewSetMixin, GenericAPIView),该分支严格满足资源接口规范
        
        ViewSet(ViewSetMixin, APIView),该分支满足的接口与资源Model类关系不是特别密切:登录接口、短信验证码接口
        

    (5)配套路由:SimpleRouter

    • 与视图家族对应的路由层SimpleRouter
    • 实现对同资源的不同操作对应url的简化,同资源只用书写一个url
    • 用法见下面:二的3中的代码中的。

    2. DRF的视图家族的作用和注意点

    (1)视图家族的作用

    • 为我们封装了资源操作中的六大接口:
      • 单查,群查
      • 单增
      • 整体单改
      • 局部单改
      • 单删

    (2)视图家族的不足之处和注意点

    • 封装的6大接口内部的逻辑都已经完成了。但另外的4大接口没有提供,需要我们自己书写方法和逻辑

    • 视图家族封装的6大接口的响应数据中只包含资源数据,没有数据状态码和状态信息,需要我们重写响应方法

    • 在6大接口中,删除接口直接删除的是资源本身,因此需要我们重写单删和群删接口

    • 在自己配置类属性queryset时,最后要加上 all 方法
      例子:models.Car.objects.filter(is_delete=False).all()
      

    3. 视图家族的使用方法

    • 共有4种使用方式
    1. 直接使用视图类APIView,自己书写操作资源的十大接口和响应方法
    
    2. 自定义视图类GenericAPIView和视图工具的组合再使用
    
    3. 直接使用工具视图类(其实和第二种方式类似,只是第二种是我们自己手动组合,自己联合使用;第三种是工具视图类已经分别组合好了的5种独立接口和4个组合接口,其中update独立接口包含整体改和局部改)
    
    4. 使用视图集中的ModelViewSet类,该类中包含6大接口(实际开发中最常用的使用方式)
    
    
    # *******************使用2,3,4方式时的相同点:(重点)********************
    
    他们都必须要配置queryset类属性和serializer_class类属性,有时也要配置lookup_url_kwarg类属性。
    

    二、视图家族的使用实例

    1. 自定义视图类GenericAPIView和视图工具的组合

    from rest_framework.mixins import RetrieveModelMixin, ListModelMixin, CreateModelMixin
    from rest_framework.generics import GenericAPIView
    
    class CarReadCreateGenericAPIView(ListModelMixin, RetrieveModelMixin, CreateModelMixin, GenericAPIView):
        queryset = models.Car.objects.filter(is_delete=False).all()
        serializer_class = serializers.CarModelSerializer
        lookup_url_kwarg = 'pk'
        
        # 群查
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
        
        # 单增
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)
    

    2. 直接使用工具视图类

    # 独立单查接口
    from rest_framework.generics import RetrieveAPIView
    class CarRetrieveAPIView(RetrieveAPIView):
        queryset = models.Car.objects.filter(is_delete=False).all()
        serializer_class = serializers.CarModelSerializer
        lookup_url_kwarg = 'pk'
    
    # 独立群查接口
    from rest_framework.generics import ListAPIView
    class CarListAPIView(ListAPIView):
        queryset = models.Car.objects.filter(is_delete=False).all()
        serializer_class = serializers.CarModelSerializer
    
    
    # 组合接口:单查(get)、单整体改(put)、单局部改(patch)、单删接口(delete)
    from rest_framework.generics import RetrieveUpdateDestroyAPIView
    class CarRetrieveUpdateDestroyAPIView(RetrieveUpdateDestroyAPIView):
        queryset = models.Car.objects.filter(is_delete=False).all()
        serializer_class = serializers.CarModelSerializer
    

    3. 使用视图集

    # 组合接口的使用
    from rest_framework.viewsets import ViewSetMixin, GenericViewSet, ViewSet, ModelViewSet
    
    # 完成资源操作的6大接口
    class CarModelViewSet(ModelViewSet):
        
        queryset = models.Car.objects.filter(is_delete=False).all()
        serializer_class = serializers.CarModelSerializer
        
        
        
        
    # 视图集的urls文件中:
    
    # 写法一:
    from django.conf.urls import url, include
    
    from . import views
    
    urlpatterns = [
        
        url(r'^cars/$', views.CarModelViewSet.as_view({
            'get': 'list',
            'post': 'create',
            'put': 'many_update',
            'patch': 'many_partial_update',
            'delete': 'many_destroy',
        })),
        url(r'^cars/(?P<pk>d+)/$', views.CarModelViewSet.as_view({
            'get': 'retrieve',
            'put': 'update',
            'patch': 'partial_update',
            'delete': 'destroy',
        })),
          
    ]
    
    # 写法二:
    # 使用与视图家族对应的路由类SimpleRouter来写路由
    # 路由层:外面会遇到这种写法,看到了要认识
    
    from django.conf.urls import url, include
    from . import views
    from rest_framework.routers import SimpleRouter
    
    router = SimpleRouter()
    router.register('v7/cars', views.CarModelViewSet, basename='car')  # cars后面没有 / 
    
    urlpatterns = [
        
    	url(r'', include(router.urls))
          
    ]
    
    

    三、视图家族的使用优化

    • 因为视图家族有他的不足之处,如下
    # 分析:从实际开发角度分析不合理点
    1)没有群增,群整体改,群局部改,群删四个接口
    2)删除操作视图集默认走的destroy方法是将资源从数据库中删除,实际通常删除时通常是对字段is_delete做修改,表示删除
    3)响应的结果只有数据,没有数据状态码和状态信息
    
    • 解决方法
    # 自定义响应模块(包含数据状态码,状态信息和数据)
    
    from rest_framework.response import Response
    class APIResponse(Response):
        def __init__(self, status=0, msg='ok', results=None, http_status=None,
            headers=None, exception=False, content_type=None, **kwargs):
            # 将status、msg、results、kwargs格式化成data
            data = {
                'status': status,
                'msg': msg,
            }
            # results只要不为空都是数据:False、0、'' 都是数据 => 条件不能写if results
            if results is not None:
                data['results'] = results
            # 将kwargs中额外的k-v数据添加到data中
            data.update(**kwargs)
    
            super().__init__(data=data, status=http_status, headers=headers, exception=exception, content_type=content_type)
    
            
    
    解决1(自己写4个群操作接口)
    # 群整体改,群局部改,全删三个接口可以独立成三个方法
    def many_update(self, request, *args, **kwargs):
        return APIResponse(msg='这个地方是群整体改,你会写!')
    def many_partial_update(self, request, *args, **kwargs):
        return APIResponse(msg='这个地方是群局部改,你会写!')
    def many_destroy(self, request, *args, **kwargs):
        return APIResponse(msg='这个地方是群删,你会写!')
    
    # 群增与单增必须公用一个接口,都要走create方法,所以重写create方法,用逻辑进行拆分
    def create(self, request, *args, **kwargs):
        request_data = request.data
        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()
        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)
    
  • 相关阅读:
    台州 OJ 3847 Mowing the Lawn 线性DP 单调队列
    洛谷 OJ P1417 烹调方案 01背包
    快速幂取模
    台州 OJ 2649 More is better 并查集
    UVa 1640
    UVa 11971
    UVa 10900
    UVa 11346
    UVa 10288
    UVa 1639
  • 原文地址:https://www.cnblogs.com/Mcoming/p/12121856.html
Copyright © 2011-2022 走看看