• drf 视图源码详解


    mixin类和Generic类

    导入

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

    CreateModelMixin 创建

    源码路径为 rest_framework —> mixing.py —>CreateModelMixin

    class CreateModelMixin(object):
        """
        Create a model instance.
        """
        def create(self, request, *args, **kwargs):
            # 传入前端传入的数据,进行反序列化
            serializer = self.get_serializer(data=request.data)
            
            # 判断是否全部都校验通过 通过了就执行下面语句
            # raise_exception=True 不通过直接报错,下面也不会执行
            serializer.is_valid(raise_exception=True)
            
            # 执行perform_create 实际就是.save() 保存
            self.perform_create(serializer)
            
            # 头部信息忽略
            headers = self.get_success_headers(serializer.data)
            
            # 遵循规范返回创建对象的数据
            # status 状态信息,对应的是一个个状态码
            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 {}
    

    视图使用-post

    class PublishView(CreateModelMixin,GenericAPIView):
      	queryset = models.Publish.objects.all()
        
      	# 内部执行的时候CreateModelMixin调用了serializer_class
        serializer_class = PublishSerializers
        
      		# 执行post请求就说明要添加数据
        def post(self, request, *args, **kwargs):
          # 执行create 自己没有就执行CreateModelMixin 
          # 也就是上面的CreateModelMixin源码解析
            return self.create(request, *args, **kwargs)
    

    ListModelMixin - 查看多条数据

    源码路径为 rest_framework —> mixing.py —>ListModelMixin

    class ListModelMixin(object):
        """
        List a queryset.
        """
        def list(self, request, *args, **kwargs):
            # 过滤相关
            # 这个时候调用了GenericAPIView内的get_queryset
            queryset = self.filter_queryset(self.get_queryset())
            
            # 分页相关
            page = self.paginate_queryset(queryset)
    
            # page不为空的情况下
            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)
    

    视图-get-取所有数据

    class PublishView(ListModelMixin,GenericAPIView):
      	queryset = models.Publish.objects.all()
        serializer_class = PublishSerializers
        
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
    

    RetrieveModelMixin 获取单条数据

    源码路径为 rest_framework —> mixing.py—>RetrieveModelMixin

    class RetrieveModelMixin(object):
    
        def retrieve(self, request, *args, **kwargs):
            # 获取单条
            instance = self.get_object()
            # 获取到序列化返回
            serializer = self.get_serializer(instance)
            return Response(serializer.data)
    

    视图-get-取指定数据

    class PublishDetailView(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
        queryset = models.Publish.objects.all()
        serializer_class = PublishSerializers
    
        # 获取指定一条
        def get(self, request, *args, **kwargs):
            return self.retrieve(request, *args, **kwargs)
    

    UpdateModelMixin 更新单条数据

    源码路径为 rest_framework —> mixing.py—>UpdateModelMixin

    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):
                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)
    

    视图 - put- 更新指定数据

    class PublishDetailView(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
        queryset = models.Publish.objects.all()
        serializer_class = PublishSerializers
    
        # 更新指定数据
        def put(self, request, *args, **kwargs):
            return self.update(request, *args, **kwargs)
    

    DestroyModelMixin - 删除指定数据

    class DestroyModelMixin(object):
        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()
    

    视图 - delete- 删除指定数据

    class PublishDetailView(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
        queryset = models.Publish.objects.all()
        serializer_class = PublishSerializers
        
        # 删除指定数据
        def delete(self, request, *args, **kwargs):
            return self.destroy(request, *args, **kwargs)
    

    GenericAPIView 通用的apiview

    get_queryset(self):

    def get_queryset(self):
        assert self.queryset is not None, (
            "'%s' should either include a `queryset` attribute, "
            "or override the `get_queryset()` method."
            % self.__class__.__name__
        )
        # 这个时候的queryset 是传递进来的queryset对象
        queryset = self.queryset
        # 判断是否是QuerySet,不是QuerySet 自动.all()
        # 说明queryset可以直接给它传递一个对象如 models.Publish.objects
        if isinstance(queryset, QuerySet):
            # Ensure queryset is re-evaluated on each request.
            queryset = queryset.all()
            # 返回queryset对象
        return queryset
    

    generics 里面组合了mixins里面方法和GenericAPIView

    导入

    from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView
    

    url

    url(r'^publish/$', views.publishView.as_view()),
    url(r'^publish/(?P<pk>d+)/$', views.PublishDetailView.as_view())
    

    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)
    

    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)
    

    视图

    from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView
    
    class publishView(ListCreateAPIView):
        queryset=models.Publish.objects.all()
        serializer_class=PublishSerializers
    
    class PublishDetailView(RetrieveUpdateDestroyAPIView):
        queryset=models.Publish.objects.all()
        serializer_class=PublishSerializers
    

    ModelViewSet 多种请求映射

    导入:

    from rest_framework.viewsets import ModelViewSet
    
    • 路由:
        url(r'^publish/$', views.PublishView.as_view({'get':'list','post':'create'})),
        url(r'^publish/(?P<pk>d+)/$', views.PublishView.as_view({'get':'retrieve','put':'update','delete':'destroy'})),
    
    • 视图:
    from rest_framework.viewsets import ModelViewSet
    
    class PublishView(ModelViewSet):
        queryset=models.Publish.objects.all()
        serializer_class=PublishSerializers
        
    # 这里面虽然可以满足方法的请求但是有时候我们会更具业务的需要,去修改,我们可以重写指定的方法,如create等
    

    为什么可以可以做到请求映射?

    • 主要是重写了as_view方法

    1.ModelViewSet中唯一不通的就是继承了GenericViewSet,上面的方法中继承的是GenericAPIView 俩者不同

    class ModelViewSet(mixins.CreateModelMixin,
                       mixins.RetrieveModelMixin,
                       mixins.UpdateModelMixin,
                       mixins.DestroyModelMixin,
                       mixins.ListModelMixin,
                       GenericViewSet):
        pass
    

    2.GenericViewSet中pass??,继承了之前的GenericAPIView,但是多继承了一个ViewSetMixin

    class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
        pass
    

    3.ViewSetMixin中仔细看as_view 和原生的Django.views.View.as_view的区别,多了一个参数

    Django.views.View.as_view
    def as_view(cls, **initkwargs):
      	pass
      
    ViewSetMixin.as_view  
    def as_view(cls, actions=None, **initkwargs):
      	pass
     
    

    4.所以路由中配置的字典{'get': 'list', 'post': 'create'}给了actions

     url(r'^publish/$', views.PublishView.as_view({'get': 'list', 'post': 'create'})),
     url(r'^publish/(?P<pk>d+)/$',
            views.PublishView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
    

    5.ViewSetMixin.as_view 源码中如果可以看出如果继承了ViewSetMixin没有传递actions直接就会异常

    if not actions:
        raise TypeError("The `actions` argument must be provided when "
                        "calling `.as_view()` on a ViewSet. For example "
                        "`.as_view({'get': 'list'})`")
    

    6.ViewSetMixin.as_view核心代码,也就是这一段代码让我们的请求直接执行对应的执行函数

    def view(request, *args, **kwargs):
        self = cls(**initkwargs)
        # 调用了传入的actions 赋值给了action_map
        self.action_map = actions
        
        # {'get': 'list', 'post': 'create'}
        # 解压赋值 请求给了method 处理参数给了action
        for method, action in actions.items():
            # self是自定义的那个路由函数的对象
            # 通过反射去对象中找action
            # handler就相当于 那个执行方法的内存地址
            handler = getattr(self, action)
            
            # 反射设置值 把执行方法的内存地址覆盖了请求,每次来请求执行的就是对于的执行函数
            setattr(self, method, handler)
    
        if hasattr(self, 'get') and not hasattr(self, 'head'):
            self.head = self.get
    
        self.request = request
        self.args = args
        self.kwargs = kwargs
    
        # And continue as usual
        return self.dispatch(request, *args, **kwargs)
    

    自定义ViewSetMixin类

    • 相当于映射 指定请求来了执行指定方法
    from rest_framework.viewsets import ViewSetMixin
    from rest_framework.views import APIView
    
    # 要注意继承的顺序如果APIView继承在前按照mro列表先执行它里面的as_views,所以要放在前面
    class Test(ViewSetMixin, APIView):
        def test(self, request, pk):
            print('自定义请求收到参数:', pk)
            return Response()
    

    url

    url(r'^test/(?P<pk>d+)/$', views.Test.as_view({'get': 'test'})),
    
  • 相关阅读:
    SpringBoot Lombok使用详解1(基本介绍、安装配置、var和val)
    SpringBoot 实现文件上传2(多文件上传、常用上传参数配置)
    SpringBoot 解决跨域请求问题(No 'AccessControlAllowOrigin' header is...)
    SpringBoot 实现静态资源的访问(附:修改过滤规则、静态资源位置)
    SpringBoot 获取Get请求参数详解(附样例:非空、默认值、数组、对象)
    SpringBoot 实现文件上传1(单文件上传、常用上传参数配置)
    SpringBoot 获取POST请求参数详解(附样例:表单数据、json、数组、对象)
    SpringBoot @ControllerAdvice的使用详解3(请求参数预处理 @InitBinder)
    SpringBoot @ControllerAdvice的使用详解1(全局异常处理 @ExceptionHandler)
    解决自动禁用Flash打开摄像头询问
  • 原文地址:https://www.cnblogs.com/limengda/p/11133779.html
Copyright © 2020-2023  润新知