Tiny_Lu
不忘初心

Day 75 视图类/视图工具类/工具视图类/视图集

视图家族

视图类

APIView和GenericAPIView

GenericAPIView中比较重要的两个方法:

  • get_object

    • 获取单查对象
  • get_serializer

    • 获取单查对象的序列化
from rest_framework.generics import GenericAPIView

class CarGenericAPIView(GenericAPIView):
    # 
    queryset = models.Car.objects.filter(is_delete=False).all()
    serializer_class = serializers.CarModelSerializer
    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)

GenericAPIView中query_set默认为None,需要自己进行赋值

注: models.Car.objects 是Manager对象,管理QuerySet

Mixins视图工具类

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

原因: 六个方法的实现体,调用的方法就是GenericAPIView提供的

  • ListModelMixin

    群查功能源码

    class ListModelMixin:
        """
        List a queryset.
        """
        def list(self, request, *args, **kwargs):
            queryset = self.filter_queryset(self.get_queryset())
    
            page = self.paginate_queryset(queryset)
            if page is not None:
                serializer = self.get_serializer(page, many=True)
                return self.get_paginated_response(serializer.data)
    
            serializer = self.get_serializer(queryset, many=True)
            return Response(serializer.data)
    
  • RetrieveModelMixin

    单查功能源码

    class RetrieveModelMixin:
        """
        Retrieve a model instance.
        """
        def retrieve(self, request, *args, **kwargs):
            instance = self.get_object()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)
    
  • CreateModelMixin

    单增功能源码

    class CreateModelMixin:
        """
        Create a model instance.
        """
        def create(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    
        def perform_create(self, serializer):
            serializer.save()
    
        def get_success_headers(self, data):
            try:
                return {'Location': str(data[api_settings.URL_FIELD_NAME])}
            except (TypeError, KeyError):
                return {}
    
  • DestroyModelMixin

    单删功能源码

    class DestroyModelMixin:
        """
        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()
    
  • UpdateModelMixin

    整体或局部单改功能源码

    class UpdateModelMixin:
        """
        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)
    

    视图工具类的使用

    from rest_framework.mixins import RetrieveModelMixin, ListModelMixin, CreateModelMixin
    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):
            # car_query = self.get_queryset()
            # car_ser = self.get_serializer(car_query, many=True)
            # return APIResponse(results=car_ser.data)
            return self.list(request, *args, **kwargs)
        # """
    
        # 单查
        """
        def get(self, request, *args, **kwargs):
            # car_obj = self.get_object()
            # car_ser = self.get_serializer(car_obj)
            # return APIResponse(results=car_ser.data)
            response = self.retrieve(request, *args, **kwargs)
            return APIResponse(results=response.data)
        """
    
        # 单增
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)
    

Generic工具视图类

工具视图类就是将不同种类的mixins与GenericAPIView进行组合,一共有9个类(九种组合)

(单查群查不能在一起)

只需要我们配置三个类属性即可: queryset, serializer_class, lookup_url_kwarg

  • CreateAPIView

    class CreateAPIView(mixins.CreateModelMixin,
                        GenericAPIView):
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)
    
  • ListAPIView

    class ListAPIView(mixins.ListModelMixin,
                      GenericAPIView):
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
    
  • RetrieveAPIView

    class RetrieveAPIView(mixins.RetrieveModelMixin,
                          GenericAPIView):
        def get(self, request, *args, **kwargs):
            return self.retrieve(request, *args, **kwargs)
    
  • DestroyAPIView

    class DestroyAPIView(mixins.DestroyModelMixin,
                         GenericAPIView):
        def delete(self, request, *args, **kwargs):
            return self.destroy(request, *args, **kwargs)
    
  • UpdateAPIView

    class UpdateAPIView(mixins.UpdateModelMixin,
                        GenericAPIView):
        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)
    
  • ListCreateAPIView

    class ListCreateAPIView(mixins.ListModelMixin,
                            mixins.CreateModelMixin,
                            GenericAPIView):
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
    
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)
    
  • RetrieveUpdateAPIView

    class RetrieveUpdateAPIView(mixins.RetrieveModelMixin,
                                mixins.UpdateModelMixin,
                                GenericAPIView):
        def get(self, request, *args, **kwargs):
            return self.retrieve(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)
    
  • RetrieveDestroyAPIView

    class RetrieveDestroyAPIView(mixins.RetrieveModelMixin,
                                 mixins.DestroyModelMixin,
                                 GenericAPIView):
        def get(self, request, *args, **kwargs):
            return self.retrieve(request, *args, **kwargs)
    
        def delete(self, request, *args, **kwargs):
            return self.destroy(request, *args, **kwargs)
    
  • RetrieveUpdateDestroyAPIView

    class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
                                       mixins.UpdateModelMixin,
                                       mixins.DestroyModelMixin,
                                       GenericAPIView):
        def get(self, request, *args, **kwargs):
            return self.retrieve(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):
            return self.destroy(request, *args, **kwargs)
    

视图集

核心: 视图集都继承了 ViewSetMixin 类,该类重写了as_view方法,相比APIView的as_view方法,额外多出了参数actions(解决如单查群查能放在同一个类中的问题)

  • ViewSet

    该分支满足的接口与资源Model类关系不是特别密切:登录接口, 短信验证码接口

  • GenericViewSet

    该分支严格满足资源接口,与数据库进行交互

  • ModelViewSet

    能满足六大接口,只需要提供两个类属性

  • ReadOnlyModelViewSet

    只满足单查群查接口

视图集产生的问题

  1. 没有群增,群整体改,群局部改,群删四个接口
  2. 删除操作视图集默认走的detroy方法是将资源从数据库中删除,通常是做一个字段is_delete字段修改表示删除
  3. 响应的结构只有数据,没有数据状态码和状态信息

解决:

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):
        request_data = request.data
        try:
            pks = []
            for dic in request_data:
                pk = dic.pop('pk')
                pks.append(pk)
            book_query = models.Car.objects.filter(is_delete=False, pk__in=pks).all()
            if len(pks) != len(book_query):
                raise Exception('pk对应的数据不存在')
        except Exception as e:
            return Response({'detail': '%s' % e}, status=400)

        car_ser = self.get_serializer(instance=book_query, data=request_data, many=True)

        car_ser.is_valid(raise_exception=True)
        car_list = car_ser.save()
        return APIResponse(results=self.get_serializer(car_list, many=True).data)
    
    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) 
posted @ 2019-12-31 10:00  二二二二白、  阅读(174)  评论(0编辑  收藏  举报