drf02 深入探索 APIView源码

一、APIView源码剖析

1.CBV源码分析

1.1 引入View

# 先读View的源码
from django.views import View
class Books(View):
    def get(self,request):
        return HttpResponse('ok')
def book(request):
    ...
    
# urls.py
path('book_fbv/', views.book),  # fvc 是通过拿到函数的内存地址
path('books_cbv/', views.Books.as_view())  # 那么as_view() 方式拿到的也是一个函数的内存地址
放了一个view的内存地址(View--》as_view--》内层函数)

1.2 View的as_view()方法

# 这是一个闭包函 :在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
@classonlymethod
def as_view(cls, **initkwargs):
    def view(request, *args, **kwargs):
        # cls 拿到的是调用这个方法的类 也就是 Books 
        # 然后根据这个调用类 生成一个对象
        self = cls(**initkwargs)
        # 用这个对象去调用 dispatch 方法 
        # self 三步走 对象(无) 调用类(无) 父类(有一个dispatch方法)
        return self.dispatch(request, *args, **kwargs)

    view.view_class = cls
    view.view_initkwargs = initkwargs
    return view

1.3 View的dispatch()方法

 http_method_names =["get","post","put","patch","delete","head","options","trace",]
 def dispatch(self, request, *args, **kwargs):
      # 用来判断是否有这些方法
      if request.method.lower() in self.http_method_names:
        # 通过反射 getattr方法去拿结果,如果有,返回该方法的函数的内存地址,否则执行其它方法
        # 比如 你的请求是get方法 
        # self 三步走 对象(无) 调用类(get方法) 父类(无)
        # hanler 拿到 get的内存地址
          handler = getattr(
              self, request.method.lower(), self.http_method_not_allowed
           )
      else:
          handler = self.http_method_not_allowed
     # 得到方法后 加括号调用该方法,得到其return方法,返回给view() 然后再返回给 as_view()
     # 调用get方法 get(request) 返回 return HttpResponse('ok')
     return handler(request, *args, **kwargs) 

2.APIView源码分析

2.1引入APIView

# 先读View的源码
from rest_framework.views import APIView
class BooksAPIView(APIView):

 def post(self, request):
        print(request.data)  # urlencoded方式有数据,json格式也有,formdata也有数据
        print(request.data.get('name'))
        print(type( request.FILES.get('file')))
        with open('xx.png','wb') as f:
            for i in request.FILES.get('file'):
                f.write(i)
        # print(type(request.POST))  # 原生的POST
        # print(request._request.POST)

        from django.http.request import QueryDict

        return HttpResponse('ok')

    ...
    
# urls.py
path('books_apiview/', views.BooksAPIView.as_view())  # 看到as_view() 可以直到拿到的应该是函数的内存地址。

2.2 APIView的as_view()方法

fromdjango.views.generic.base.View
class APIView(View):
	@classmethod
	  def as_view(cls, **initkwargs):
		  # 获取父类 View 的as_view() - 需要再走一遍CBV源码 不同之处就在其中
	      view = super().as_view(**initkwargs)
          # 为什么view 是一个函数,还能添加属性呢?一切皆对象
	      view.cls = cls
	      view.initkwargs = initkwargs
	
		  # from django.views.decorators.csrf import csrf_exempt
          # csrf: 请求伪造   csrf_exempt: 全局加 局部不加
          # 这段代码是什么意思呢? 表示所有APIView.as_view() 的方法都不用csrf方法
	      return csrf_exempt(view)	

2.3 View的as_view()方法

@classonlymethod
def as_view(cls, **initkwargs):
    def view(request, *args, **kwargs):
        # cls 拿到的是调用这个方法的类 也就是 Books 
        # 然后根据这个调用类 生成一个对象
        self = cls(**initkwargs)
        # 关键变化所在
        # 用这个对象去调用 dispatch 方法 
        # self 三步走 对象(无) 调用类(无) 父类(APIView.dispatch) 父类的父类(有)
        return self.dispatch(request, *args, **kwargs)

    view.view_class = cls
    view.view_initkwargs = initkwargs
    return view

2.4 APIView的dispatch()方法

 def dispatch(self, request, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
        # 这个是重点 - 重新包装成一个request对象,以后再用的request对象,就是新的request对象了
        # 总结原来的方法还能向原来的方式去用,并封装了其它的方法 如data query_params()
        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

            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

2.4.1 APIView的dispatch().initialize_request()
def initialize_request(self, request, *args, **kwargs):
	# 对原本的request数据进行封装
     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
     )




from rest_framework.request import Request
# 只要继承了APIView,视图类中的request对象,都是新的,也就是上面那个request的对象了
# 老的request在新的request._request
# 以后使用reqeust对象,就像使用之前的request是一模一样(因为重写了__getattr__方法)
  def __getattr__(self, attr):
        try:
            return getattr(self._request, attr) #通过反射,取原生的request对象,取出属性或方法
        except AttributeError:
            return self.__getattribute__(attr)

 # request.data 感觉是个数据属性,其实是个方法,@property,修饰了
	它是一个字典,post请求不管使用什么编码,传过来的数据,都在request.data
 #get请求传过来数据,从哪取?
	request.GET
    @property
    def query_params(self):
        """
        More semantically correct name for request.GET.
        """
        return self._request.GET
    
    #视图类中
     print(request.query_params)  #get请求,地址中的参数
     # 原来在
     print(request.GET)





2.4.2 APIView的dispatch().initial()
def initial(self, request, *args, **kwargs):
    # 控制api版本,如果需要控制版本可以使用这方法
    version, scheme = self.determine_version(request, *args, **kwargs)
    request.version, request.versioning_scheme = version, scheme

    # 认证组件:校验用户 - 游客、合法用户、非法用户
    # 游客:代表校验通过,直接进入下一步校验(权限校验)
    # 合法用户:代表校验通过,将用户存储在request.user中,再进入下一步校验(权限校验)
    # 非法用户:代表校验失败,抛出异常,返回403权限异常结果
    self.perform_authentication(request)
    # 权限组件:校验用户权限 - 必须登录、所有用户、登录读写游客只读、自定义用户角色
    # 认证通过:可以进入下一步校验(频率认证)
    # 认证失败:抛出异常,返回403权限异常结果
    self.check_permissions(request)
    # 频率组件:限制视图接口被访问的频率次数 - 限制的条件(IP、id、唯一键)、频率周期时间(s、m、h)、频率的次数(3/s)
    # 没有达到限次:正常访问接口
    # 达到限次:限制时间内不能访问,限制时间达到后,可以重新访问
    self.check_throttles(request)

2.5总结 - 流程图

posted @ 2023-05-25 11:15  派森的猫  阅读(16)  评论(0)    收藏  举报