• 1230 视图家族类


    视图家族

    1. 两个基类 views 视图类

    1.1 APIView

    rest_framework.views.APIView
    
    APIView是REST framework提供的所有视图的基类,继承自Django的View父类。
    View: 将请求方式与视图类的同名方法建立映射,完成请求响应
    

    特点

    • 拥有view的所有功能
    • 重写了as_view 局部禁用csrf
    • 重写dispatch 请求,响应,渲染,异常,解析,三大认证
    • 一系列类属性 可以完成视图类的局部配置

    View的不同

    APIViewView的不同之处在于:

    - 传入到视图方法中的是REST framework的`Request`对象,而不是Django的`HttpRequeset`对象;
    - 视图方法可以返回REST framework的`Response`对象,视图会为响应数据设置(render)符合前端要求的格式;
    - 任何`APIException`异常都会被捕获到,并且处理成合适的响应信息;
    - 在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
    

    APIView中仍以常规的类视图定义方法来实现get() 、post(),或者其他请求方式的方法。

    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    # url(r'^books/$', views.BookListView.as_view()),
    class BookListView(APIView):
        def get(self, request):
            books = BookInfo.objects.all()
            serializer = BookInfoSerializer(books, many=True)
            return Response(serializer.data)
    

    1.2 GenericAPIView

    from rest_framework.generics import GenericAPIView
    

    特点

    继承自APIVIew,增加了对于列表视图和详情视图可能用到的通用支持方法。主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。
    
    • 继承APIView,所以拥有APIView的所有
    • get_queryset方法,配置queryset类属性,提供类相关的models
    • 在第二条基础上,get_object方法,配置lookup_url_kwargs类属性,提供视图类相关的具体model
    • get_serializer方法,配置serializer_class类属性,提供视图类相关的序列化对象

    提供的方法

    • get_object()
    get_object(self) 返回详情视图所需的模型类数据对象,默认使用lookup_field参数来过滤queryset。 在试图中可以调用该方法获取详情信息的模型类对象。
    
    若详情访问的模型类对象不存在,会返回404。
    
    该方法会默认使用APIView提供的check_object_permissions方法检查当前对象是否有权限被访问。
    
    • get_serializer()
    返回序列化器对象,被其他视图或扩展类使用,如果我们在视图中想要获取序列化器对象,可以直接调用此方法。
    
    注意,在提供序列化器对象的时候,REST framework会向对象的context属性补充三个数据:request、format、view,这三个数据对象可以在定义序列化器时使用。
    
    • get_queryset()
    返回视图使用的查询集,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写
    

    代码

    .all()不能直接写到objects结束,因为objects结束时,不是QuerySet对象,而是Manager对象,但.all()和.filter()后一定会是QuerySet对象
    
    from rest_framework.generics import GenericAPIView
    class CarGenericAPIView(GenericAPIView):
        queryset = models.Car.objects.all()
        serializer_class = serializers.CarModelSerializer
        # GenericAPIView的群查
        def get(self,request,*args,**kwargs):
            # car_query = models.Car.objects.filter(is_delete=False).all()
            # 1. 封装了获取queryset对象的get方法,首先要定义
            car_query = self.get_queryset()
            # car_ser = serializers.CarModelSerializer(instance=car_query,many=True)
            # 2. 封装了序列化类,但是也需要指定serializer_class序列化类
            car_ser = self.get_serializer(instance=car_query,many=True)
            return APIResponse(results=car_ser.data)
        
            # 单查
        def get(self,request,*args,**kwargs):
            # pk = kwargs.get('pk')
            # car_obj = models.Car.objects.filter(is_delete=False,pk=pk).first()
            # 通过get_object获取单查的obj对象
            car_obj = self.get_object()
            # car_ser = serializers.CarModelSerializer(car_obj)
            car_ser = self.get_serializer(car_obj)
            return APIResponse(results=car_ser.data)
    
    • lookup_url_kwarg属性
    # 1.url单查时系统默认设置有名分组是'pk',也可更改并自己配置(唯一键的字段名)
    lookup_url_kwarg = 'id'     # 使用id当做分组查询url
    

    总结

    • GenericAPIView就是在APIView基础上额外提供了三个方法,三个类属性,如果不配合视图工具类,体现不出优势
    • 目的:视图中的增删改查逻辑相似,但操作的资源不一致,操作资源就是操作 资源对象们、资源对象以及资源相关的序列化类,
    • 将这三者形成配置,那操作逻辑就一致,就可以进行封装

    2. 五个扩展类 mixins 视图工具类

    from rest_framework.mixins import 
    RetrieveModelMixin,
    ListModelMixin,
    CreateModelMixin,
    UpdateModelMixin,
    DestroyModelMixin
    

    使用

    # mixins视图工具类
    from rest_framework.generics import mixins
    class CarMixinGenericAPIView(mixins.ListModelMixin,mixins.CreateModelMixin,GenericAPIView):
        queryset = models.Car.objects.all()
        serializer_class = serializers.CarModelSerializer
        # 群查
        def get(self,request,*args,**kwargs):
            # ListModelMixin中的list与之前的一样,所以直接使用
            return self.list(request,*args,**kwargs)
        
        # 单查 (单查与群查不能同时存在)
        def get(self,request,*args,**kwargs):
            # ListModelMixin中的list与之前的一样,所以直接使用
            return self.retrieve(request,*args,**kwargs)
    
        # 增
        def post(self,request,*args,**kwargs):
            # CreateModelMixin的create与之前的一样,所以直接使用
            return self.create(request,*args,**kwargs)
    

    特点

    五个类,六个方法
    
    - 要配合GenericAPIView类使用,将单查、群查、单增,单整体改,单局部改,单删六个接口,封装成retrieve、list、create、update、partial_update、destroy六个方法
    

    原因

    六个方法的实现体,调用的方法就是GenericAPIView提供的,所以要配合GenericAPIView类使用
    

    2.1 ListModelMixin (群查)

    列表视图扩展类,提供list(request, *args, **kwargs)方法快速实现列表视图,返回200状态码。

    该Mixin的list方法会对数据进行过滤和分页。

    # 群查
        def get(self,request,*args,**kwargs):
            # ListModelMixin中的list与之前的一样,所以直接使用
            return self.list(request,*args,**kwargs)
    

    2.2 CreateModelMixin (单增)

    创建视图扩展类,提供create(request, *args, **kwargs)方法快速实现创建资源的视图,成功返回201状态码。

    如果序列化器对前端发送的数据验证失败,返回400错误。

    # 增
        def post(self,request,*args,**kwargs):
            # CreateModelMixin的create与之前的一样,所以直接使用
            return self.create(request,*args,**kwargs)
    

    2.3 RetrieveModelMixin (单查)

    详情视图扩展类,提供retrieve(request, *args, **kwargs)方法,可以快速实现返回一个存在的数据对象。

    如果存在,返回200, 否则返回404。

    class BookDetailView(RetrieveModelMixin, GenericAPIView):
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
        def get(self, request, pk):
            return self.retrieve(request)
    

    2.4 UpdateModelMixin (局部单改)

    更新视图扩展类,提供update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象。

    同时也提供partial_update(request, *args, **kwargs)方法,可以实现局部更新。

    成功返回200,序列化器校验数据失败时,返回400错误。

    源代码:

    class UpdateModelMixin(object):
        """
        Update a model instance.
        """
        def update(self, request, *args, **kwargs):
            partial = kwargs.pop('partial', False)
            instance = self.get_object()
            serializer = self.get_serializer(instance, data=request.data, partial=partial)
            serializer.is_valid(raise_exception=True)
            self.perform_update(serializer)
    
            if getattr(instance, '_prefetched_objects_cache', None):
                # If 'prefetch_related' has been applied to a queryset, we need to
                # forcibly invalidate the prefetch cache on the instance.
                instance._prefetched_objects_cache = {}
    
            return Response(serializer.data)
    
        def perform_update(self, serializer):
            serializer.save()
    
        def partial_update(self, request, *args, **kwargs):
            kwargs['partial'] = True
            return self.update(request, *args, **kwargs)
    

    2.5 DestroyModelMixin (删除)

    直接删除数据对象,不推荐

    删除视图扩展类,提供destroy(request, *args, **kwargs)方法,可以快速实现删除一个存在的数据对象。

    成功返回204,不存在返回404。

    class DestroyModelMixin(object):
        """
        Destroy a model instance.
        """
        def destroy(self, request, *args, **kwargs):
            instance = self.get_object()
            self.perform_destroy(instance)
            return Response(status=status.HTTP_204_NO_CONTENT)
    
        def perform_destroy(self, instance):
            instance.delete()
    

    3. 可用子类视图 generics 工具视图类

    from rest_framework import generics
    
    • 帮我们将不同个数不同种类的mixins与GenericAPIView进行组合
    • 不同的组合帮我们实现好对应的get、post、put、patch、delete方法
    • 需要我们自己配置三个类属性即可:queryset、serializer_class、lookup_url_kwarg
    # 工具视图类 (最常用的视图)
    from rest_framework.generics import CreateAPIView,ListAPIView,ListCreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView
    # CreateAPIView 继承了 mixins.CreateModelMixin,GenericAPIView
    #     CreateAPIView中帮我们写了post方法     (单增)
    #     ListAPIView写了get方法                (群查)
    # 而ListCreateAPIView中也包含,所以直接继承,单查,群查,单改,单删一起继承即可
    class CarListCreateAPIView(CreateAPIView,ListAPIView,ListCreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView):
        queryset = models.Car.objects.filter(is_delete=False).all()
        serializer_class = serializers.CarModelSerializer
        # url单查时系统默认设置有名分组是'pk',也可更改并自己配置(唯一键的字段名)
        lookup_url_kwarg = 'id'     # 使用id当做分组查询url
        # 1.单查时都不需要写get方法继承使用,直接配置queryset与serializer_class即可
    
        # 2. 删除时,destroy方法默认是字段删除,所以需要重写destroy方法
        #     有删除需求的接口继承DestroyAPIView,重写destroy方法完成字段的删除(is_delet=False)
        def destroy(self, request, *args, **kwargs):
            pass
    
    # 单独完成单查接口
    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 = 'id'     # 使用id当做分组查询url
    
    # 单独完成群查接口
    from rest_framework.generics import ListAPIView
    class CarListAPIView(ListAPIView):
        queryset = models.Car.objects.filter(is_delete=False).all()
        serializer_class = serializers.CarModelSerializer
    
    

    1)CreateAPIView

    提供 post 方法

    继承自: GenericAPIView、CreateModelMixin

    2)ListAPIView (群查)

    提供 get 方法

    继承自:GenericAPIView、ListModelMixin

    # 单独完成群查接口
    from rest_framework.generics import ListAPIView
    class CarListAPIView(ListAPIView):
        queryset = models.Car.objects.filter(is_delete=False).all()
        serializer_class = serializers.CarModelSerializer
    

    3)RetireveAPIView (单查)

    提供 get 方法

    继承自: GenericAPIView、RetrieveModelMixin

    # 单独完成单查接口
    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 = 'id'     # 使用id当做分组查询url
    

    4)DestoryAPIView

    提供 delete 方法

    继承自:GenericAPIView、DestoryModelMixin

    5)UpdateAPIView

    提供 put 和 patch 方法

    继承自:GenericAPIView、UpdateModelMixin

    6)RetrieveUpdateAPIView

    提供 get、put、patch方法

    继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin

    7)RetrieveUpdateDestoryAPIView 单查,单整体改,单局部改,单删

    提供 get、put、patch、delete方法

    继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin

    8)ListCreateAPIView (群查和单增)

    推导代码

    class CarView(APIView):
        def get(self):
            obj,
            ser,
            response,
            
    class CarView(GenericAPIView):  # 不会出现,中间产物
        queryset,
        serializer_class,
        lookup_url_kwarg,
        def get(self):
            obj,
            ser,
            response,
    
    class CarView(RetrieveModelMixin, GenericAPIView):  # 自定义组合,可以超过九种
        queryset,
        serializer_class,
        lookup_url_kwarg,
        def get(self):
            self.retrieve()
            
    class CarView(RetrieveAPIView):  # 最终产物,系统只提供了九种组合,RetrieveAPIView是其中一种
        queryset,
        serializer_class,
        lookup_url_kwarg,
    
    

    4. 视图集 ViewSet

    from rest_framework.viewsets import ModelViewSet六大功能接口

    # 视图集
    
    # 1.viewsetmixin重写了as_view方法,解决单查群查问题
    from rest_framework.viewsets import ModelViewSet
    # ModelViewSet中继承了viewsetmixin和工具视图类的所有类
    class CarModelViewSet(ModelViewSet):
        queryset = models.Car.objects.filter(is_delete=False).all()
        serializer_class = serializers.CarModelSerializer
        # 实现了六大接口
        
        # 自定义其他接口的方法
        def destroy(self,request,*args,**kwargs):
            pass
    
    # 2.在as_view中设置单查群查的接口方法
        # 将所有请求方法与响应方法名的映射关系交给用户自己配置(自定义映射关系)
        url(r'^v5/cars/$', views.CarModelViewSet.as_view({
            'get':'list',
            'post':'my_post',
        })),
        url(r'v5/cars/(?P<pk>d+)/$', views.CarModelViewSet.as_view({
            'get':'retrieve',
            'put':'update',
            'patch':'partial_update',
            'delete':'destroy'
        })),
    

    4.1 常用视图集父类

    1) ViewSet

    继承自APIView,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。

    在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。

    2)GenericViewSet

    继承自GenericAPIView,作用也与GenericAPIVIew类似,提供了get_object、get_queryset等方法便于列表视图与详情信息视图的开发。

    3)ModelViewSet

    继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

    4)ReadOnlyModelViewSet

    继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin。

    4.2 action属性

    使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:

    • list() 提供一组数据
    • retrieve() 提供单个数据
    • create() 创建数据
    • update() 保存数据
    • destory() 删除数据

    ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。

    视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上。如:

    class BookInfoViewSet(viewsets.ViewSet):
    
        def list(self, request):
            ...
    
        def retrieve(self, request, pk=None):
            ...
    

    在设置路由时,我们可以如下操作

    urlpatterns = [
        url(r'^books/$', BookInfoViewSet.as_view({'get':'list'}),
        url(r'^books/(?P<pk>d+)/$', BookInfoViewSet.as_view({'get': 'retrieve'})
    ]
    

    action属性

    在视图集中,我们可以通过action对象属性来获取当前请求视图集时的action动作是哪个。

    例如:

    # 动态设置序列化器def get_serializer_class(self):
        if self.action == 'create':
            return OrderCommitSerializer
        else:
            return OrderDataSerializer
    

    4.3 视图集自定义群操作

    # 视图集
    from rest_framework.viewsets import ViewSetMixin, GenericViewSet, ViewSet, ModelViewSet
    class CarReadOnlyAPIView(RetrieveModelMixin, ListModelMixin, GenericViewSet):
        # def many_get(self, request, *args, **kwargs):
        #     return self.list(request, *args, **kwargs)
        #
        # def single_get(self, request, *args, **kwargs):
        #     return self.retrieve(request, *args, **kwargs)
    
        queryset = models.Car.objects.filter(is_delete=False).all()
        serializer_class = serializers.CarModelSerializer
    
    
    # 六大接口
    class CarModelViewSet(ModelViewSet):
        queryset = models.Car.objects.filter(is_delete=False).all()
        serializer_class = serializers.CarModelSerializer
        # 分析:从实际开发角度分析不合理点
        # 1)没有群增,群整体改,群局部改,群删四个接口
        # 2)删除操作视图集默认走的destroy方法是将资源从数据库中删除,通常一个做字段is_delete字段修改表示删除
        # 3)响应的结果只有数据,没有数据状态码和状态信息
    
        # 解决1,
        # 群整体改,群局部改,全删三个接口可以独立成三个方法
        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方法是完成is_delete字段值修改 - 重写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)
    
    

    4.4 总结

    核心:
    	视图集都继承了 ViewSetMixin类,该类重写了as_view方法,相比APIView的as_view方法,额外多出了一个参数actions
    	
    	as_view({'get': 'list'}) 传入的{'get': 'list'}就被actions接受收,原理是将get请求映射给视图类的list函数进行处理
    
    1)为什么有GenericViewSet和ViewSet两个视图集基类
        GenericViewSet(ViewSetMixin, GenericAPIView),该分支严格满足资源接口
        ViewSet(ViewSetMixin, APIView),该分支满足的接口与资源Model类关系不是特别密切:登录接口、短信验证码接口
    
    
    2)ReadOnlyModelViewSet,ModelViewSet两个视图集子类,就是做个一堆mixin与GenericViewSet相结合,自己在urls文件中配置as_view设置映射关系
    

    5. 路由Routers

    对于视图集ViewSet我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用Routers来帮助我们快速实现路由信息。

    REST framework提供了两个router

    • SimpleRouter
    • DefaultRouter

    使用方式

    1.创建router对象,并注册视图集,例如

    # 路由层:外面会遇到这种写法,看到了要认识
    from rest_framework.routers import SimpleRouter
    router = SimpleRouter()
    
    router.register('v7/cars', views.CarModelViewSet, basename='car')
    # router.register('books', views.BookModelViewSet, basename='book')
    # router.register('users', views.UserModelViewSet, basename='user')
    

    register(prefix, viewset, base_name)

    • prefix 该视图集的路由前缀
    • viewset 视图集
    • base_name 路由名称的前缀

    如上述代码会形成的路由如下:

    ^books/$    name: book-list
    ^books/{pk}/$   name: book-detail
    

    2.添加路由数据

    可以有两种方式:

    urlpatterns = [
        url(r'', include(router.urls))
    ]
    # urlpatterns.extend(router.urls)
    

    urlpatterns = [
        ...
    ]
    urlpatterns += router.urls
    
  • 相关阅读:
    swoole 安装方法 使用即时聊天
    git的介绍以及简单应用
    curl的应用
    linux下监听和同步代码配置
    mac skim 修改背景色
    php 编译安装的一个 configure 配置
    mac mysql error You must reset your password using ALTER USER statement before executing this statement.
    yii2 控制器里 action 大小写组合造成的路由问题
    warning : json_decode(): option JSON_BIGINT_AS_STRING not implemented in xxx
    redis 自启动脚本
  • 原文地址:https://www.cnblogs.com/fwzzz/p/12122216.html
Copyright © 2020-2023  润新知