zoukankan      html  css  js  c++  java
  • CBV源码与APIView源码解析

    一、CBV源码解析

    在我们写cbv的时候在url中和fbv的区别就是是否调用了as_view()方法,所以关键入手点就是这个方法

    @classonlymethod
    	# 这是类的绑定方法,这个cls是我们创造的类
    def as_view(cls, **initkwargs):
        def view(request, *args, **kwargs):
            # self是在实例化一个对象
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
                self.request = request
                self.args = args
                self.kwargs = kwargs
                # 关键在dispatch内
                return self.dispatch(request, *args, **kwargs)
         return view
            
    
            def dispatch(self, request, *args, **kwargs):
               # http_method_names是一个存放了所有请求方式的列表
                if request.method.lower() in self.http_method_names:
                    # 用到了反射的方法吗,如果CBV中我们写了这个请求的方法,就把他赋值给handler,如果没有就返回一个not_allowed
                    handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
                    else:
                        handler = self.http_method_not_allowed
                        return handler(request, *args, **kwargs)
    

    二、APIView源码解析

    apiview同样关键点在as_view中

     @classmethod
        def as_view(cls, **initkwargs):、
            # 继承了父类View的as_view的调用结果,就是父类的view
        	view = super().as_view(**initkwargs)
            view.cls = cls
            view.initkwargs = initkwargs
            # 这是调用了屏蔽csrf检测的装饰器
            return csrf_exempt(view)
       	# 这里要注意区分,return中的view,其实就是父类中的view
        
            def view(request, *args, **kwargs):          
                    # 关键还是在dispatch内
                    # 注意此刻dispatch的查找顺序,定义的类中没有,去父类APIView中找,我们发现了新的dispatch方法
                    return self.dispatch(request, *args, **kwargs)
        
    # APIView的dispatch方法
        def dispatch(self, request, *args, **kwargs):
    
            self.args = args
            self.kwargs = kwargs
            # 重新包装成一个request对象,以后再用的request对象,就是新的request对象了
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            self.headers = self.default_response_headers  # deprecate?
    
            try:
                # 三大认证模块,在下面展示
                self.initial(request, *args, **kwargs)
    
                # Get the appropriate handler method
                # 这里和父类一样就是调用请求函数的
                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
    
                # 响应模块,调用请求函数得到一个响应对象,此时内部存放的是json数据
                response = handler(request, *args, **kwargs)
    			
            except Exception as exc:
                # 异常模块
                response = self.handle_exception(exc)
    
            # 渲染模块,我们能在前端看到api的html页面,就是这里做的
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response 
        
        # APIView的initial方法,认证模块
     	def initial(self, request, *args, **kwargs):
            # 认证组件:校验用户 - 游客、合法用户、非法用户
            # 游客:代表校验通过,直接进入下一步校验(权限校验)
            # 合法用户:代表校验通过,将用户存储在request.user中,再进入下一步校验(权限校验)
            # 非法用户:代表校验失败,抛出异常,返回403权限异常结果
            self.perform_authentication(request)
            # 权限组件:校验用户权限 - 必须登录、所有用户、登录读写游客只读、自定义用户角色
            # 认证通过:可以进入下一步校验(频率认证)
            # 认证失败:抛出异常,返回403权限异常结果
            self.check_permissions(request)
            # 频率组件:限制视图接口被访问的频率次数 - 限制的条件(IP、id、唯一键)、频率周期时间(s、m、h)、频率的次数(3/s)
            # 没有达到限次:正常访问接口
            # 达到限次:限制时间内不能访问,限制时间达到后,可以重新访问
            self.check_throttles(request)
            
        # 我们再看看新赋值的request内部是什么
        def initialize_request(self, request, *args, **kwargs):
            """
            Returns the initial request object.
            """
            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
            )
        # 再点击Request,我们发现原来的request被封装给_request了,之后使用request都是在原来基础上新造的
        # 原来的request都是.属性的,所以我们可以很肯定这里重写了点拦截方法(__getattr__)
        def __getattr__(self, attr):
            try:
                return getattr(self._request, attr) #通过反射,取原生的request对象,取出属性或方法
            except AttributeError:
                return self.__getattribute__(attr)
    

    APIView还给我们做了一件事情,因为我们后面在通过api进行数据交互的时候用的都是json格式的数据,但是django默认不会处理json格式的数据,所以这里他新定义的request有一个data方法

    @property
    def data(self):
        if not _hasattr(self, '_full_data'):
            self._load_data_and_files()
            return self._full_data
    

    他还重新定义了GET的名字,我们本来拿到get请求来的数据应该是django.GET来的,但是由于我们是查找数据,这样的名字不够见名知意所以,

        @property
        def query_params(self):
            """
            More semantically correct name for request.GET.
            """
            return self._request.GET
    

    用request.query_params来替代request.GET,当然由于之前重写request的时候用了反射的方法把原生request的方法属性全都赋值给了新的request所以GET也是能用的

  • 相关阅读:
    消费者端调用服务者端的接口,服务端抛出一个自定义异常,该异常继承了RuntimeException,但是消费者端Debug发现catch到的是RpcException,RpcExcetion为dubbo的异常,为什么服务提供者的返回的是自定义异常却变成了RpcException?
    springcloud使用常见总是总结
    mysql启动时报 --initialize specified but the data directory has files in it. Aborting.
    简介
    继承&&聚合
    maven常见命令&&依赖&&maven生命周期
    下载配置Maven&&使用maven
    Maven [ERROR] 不再支持源选项 5。请使用 6 或更高版本
    解决Maven下载速度缓慢问题
    配置和使用Maven
  • 原文地址:https://www.cnblogs.com/hz2lxt/p/13257266.html
Copyright © 2011-2022 走看看