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)
    
  • 相关阅读:
    ajax
    导入操作
    游标的使用
    多行编辑
    IOS开发之--NSPredicate
    asp.net DataTables
    asp.net 汉字转拼音的车祸现场
    Git 连接细节
    Aspose.Words 操作指北
    码云代码管理插件备忘
  • 原文地址:https://www.cnblogs.com/Mcoming/p/12121856.html
Copyright © 2011-2022 走看看