Django之CBV中View、APIView源码分析

View源码分析:

views文件:

class MyView(View):
    def get(self,request):
        return HttpResponse('ok')
    def post(self,request):
        return HttpResponse('post')

urls文件:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$',views.MyView.as_view()),
]

View源码思路分析(这里拿重要的部分源码进行分析):

'''
主要思路:View--》as_view --》as_view下的函数
当有路由匹配进来的时候,就会执行as_view这个方法
'''

class View(object):
    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']  
    #继承classmethod
    @classonlymethod
    '''
      这时会把我们在vies创建的MyView类,当作参数传递给as_view这个方法。
      相当于as_view(MyView)
      **initkwargs是url匹配传递过来的参数:例如 url(r'^$',views.MyView.as_view(age=1)),
    '''
    def as_view(cls, **initkwargs):
        """
        Main entry point for a request-response process.
        """

        def view(request, *args, **kwargs):
            '''
            self = cls(**initkwargs)
            这一步是实例化一个对象,将我们传进来的类实例化赋值给self
            相当于self=MyView(参数)
            '''
            self = cls(**initkwargs)

            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request  #将当前的request请求赋值给对象中的request.
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)
        return view

    def dispatch(self, request, *args, **kwargs):
        # request: 还是当前的请求对象
        if request.method.lower() in self.http_method_names:
            '''
            获取当前对象(MyView类的对象)的方法的内存地址
            例如get请求:handler=getattr(self,'get')
            '''
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs) #相当于执行get(request)

APIView源码分析:

 views文件:

from rest_framework.views import APIView
class MyAPIView(APIView):
    def get(self,request):
        #此时的request是drf重新封装后的request
        print(request._request)  #原生request
        # 相当于request.GET
        data=request.query_params  
        return HttpResponse('ok')
    def post(self,request):
        # 相当于request.POST
        post_data=request.data
        file_data=request.FILES

urls文件

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'apiview/',views.MyAPIView.as_view())
]

APIView源码分析:

'''
#请求来了-》路由匹配上-》view(request)-》调用了self.dispatch(),这时执行的是apiview的dispatch,而不是view的dispatch
'''
class APIView(View):

    @classmethod
    def as_view(cls, **initkwargs):
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '
                    'as the result will be cached and reused between requests. '
                    'Use `.all()` or call `.get_queryset()` instead.'
                )
            cls.queryset._fetch_all = force_evaluation
        '''
        view = super().as_view(**initkwargs)
        此时的view就View源码的as_view方法下的view
        即调用父类(view)的as_view方法。
        此时调用的dispatch方法是APIView的dispatch方法,而不是View的
        执行顺序是从当前类(MyAPIView)中查找,找不到再从父类(APIView)中查找,再找不到再从APIView的父类View中找
        '''
        view = super().as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs

        #只要继承了APIView,就不会再进行csrf认证
        return csrf_exempt(view)

    #APIView的dispatch方法。
    def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        '''
            ()参数中request是当前请求的request
            左边的request是重新封装后的request
            具体可以参照源码4
        '''
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            #三大认证模块(具体看下面源码5)
            self.initial(request, *args, **kwargs)
            # 这部分跟View类中那部分一样。
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            #响应模块
            response = handler(request, *args, **kwargs)

        except Exception as exc:
            #异常模块
            response = self.handle_exception(exc)
        #渲染模块
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response


    #源码4、重新封装request的方法
    def initialize_request(self, request, *args, **kwargs):
        parser_context = self.get_parser_context(request)
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
#源码5 APIView的initial方法 def initial(self, request, *args, **kwargs): # 做版本的控制 version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted ''' 认证组件:校验用户 - 游客、合法用户、非法用户 游客:代表校验通过,直接进入下一步校验(权限校验) 合法用户:代表校验通过,将用户存储在request.user中,再进入下一步校验(权限校验) 非法用户:代表校验失败,抛出异常,返回403权限异常结果 ''' self.perform_authentication(request) ''' 权限组件:校验用户权限 - 必须登录、所有用户、登录读写游客只读、自定义用户角色 认证通过:可以进入下一步校验(频率认证) 认证失败:抛出异常,返回403权限异常结果 ''' self.check_permissions(request) ''' 频率组件:限制视图接口被访问的频率次数 - 限制的条件(IP、id、唯一键)、频率周期时间(s、m、h)、频率的次数(3/s) 没有达到限次:正常访问接口 达到限次:限制时间内不能访问,限制时间达到后,可以重新访问 ''' self.check_throttles(request)

 

 

posted @ 2020-10-31 00:09  NQ31  阅读(88)  评论(0编辑  收藏  举报