drf CBV和APIView源码解读

CBV源码

1. 入口点:

视图
class Books(View):
 def get(self, request):
 return JsonResponse(back_dic)
from django.views import View
url(r'^books/',views.Book.as_view()) # 类调用的方法绑定方法
@classonlymethod
def as_view(cls, **initkwargs):

这里我们可以看到as_view上面加了一个类方法,去@classonlymethod它继承的classmethod的方法,这个重定义的类方法比classmethod更强大,Book.as_view(),调用的时候可以不用加括号

2. 请求来时

  @classonlymethod
      def as_view(cls, **initkwargs):
          def view(request, *args, **kwargs): # 档次请求的request,如request.method,request.POST
              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
              return self.dispatch(request, *args, **kwargs)
          view.view_class = cls
          view.view_initkwargs = initkwargs
  
          update_wrapper(view, cls.dispatch, assigned=())
          return view
      

​ 请求来了,路径匹配,执行函数的内存地址(request),会把当前请求的request对象当作第一个参数传过来,执行as_view的内层函数 def view(),cls(**initkwargs) 实例化得到一个对象,cls就是Books对象,用反射判断有没有get这个方法,self.request = request把当前请求的request赋值到Books对象里面的request,在视图函数里print(self.request)这个request和参数里面的request是一样的,指向一个内存地址。

return self.dispatch(request, *args, **kwargs),self.dispatch,self是books,所以要到当前类视图函数寻找dispatch方法,没有才去到View去找发现了dispatch方法.

 def dispatch(self, request, *args, **kwargs):

       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
       return handler(request, *args, **kwargs)

request就是当次请求的request.lower()转成小写,判断视图函数里面有没有self.http_method_names这个方法,在强调一遍这里的self始终是Book对象,没有就去父类View里面找到这个方法http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'],反射的意思: handler=getattr(self,'get'),你写的Book类的get方法的内存地址,执行get(request),原理还是FBV.

提示:如何我们只接受get请求可以在视图函数里面重写http_method_names

APIView源码

它的原理就是通过中间件之后执行的代码

1. 入口:

视图
class BooksAPIView(APIView)
	def get(self,request):
        pass
url
url(r'^books/',views.BooksAPIView.as_view()),

这里的和CBV的原理一样,但是这里的as_view,前面加了@classmethod,这里调的是APIView里面的as_view方法

	@classmethod
    def as_view(cls, **initkwargs):
        view = super().as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs
        
        return csrf_exempt(view)

我们看到super调用了父类(View)的as_view方法,最后还是CBV里面的内层函数,这里的view是父类类面return 返回的view,csrf_exempt:不管你有没有把中间件里面的csrf中间件去掉, 都没有csrf认证了,

2.dispatch

​ 我们把目光继续跳到父类view里面,执行到dispatch里面后,先去BooksAPIView去找发现没有这个函数,就绪它继承的父类APIView里面去找,我们可以找到这个dispatch方法,没有去APIView父类(View),如果想看查找顺序用mro,

  def dispatch(self, request, *args, **kwargs):
     
        self.args = args
        self.kwargs = kwargs
        # 重新包装一个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)
            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

这个是APIView的dispatch方法,请求来了在路由匹配上执行到了view里面的内层函数,调用了dispatch,按照查找顺序就到了这里面, request = self.initialize_request(request, *args, **kwargs)这个request是当次请求的request赋值给了self.initialize_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
        )

这个代码先忽略掉,self.initialize_request(request, *args, **kwargs)的request是当次请求的request, request = self.initialize_request,这个request是一个新的request对象,self.request = request把这个新的request赋值给了当前类的 request,视图函数里面的request已经不是django原生的request,是drf中间定义的request对象,我们导入from rest_framework.request import Request查看到,

class BooksAPIView(APIView)
	def get(self,request):
        print(request.data)

验证:原生的request里面没有data这个属性,这个是drf的request的data属性。

3.drf的Request类

3.1 原生request的封装

上面我们执行到request = self.initialize_request(request, *args, **kwargs)的self调用

initialize_request这个方法,到视图类里面去找没有,再到APIView里面找到了initialize_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
        )

这里return 一个Request对象,里面有一个原生的request对象,原生的request对象被封装到了Request

class Request:
    """
    Wrapper allowing to enhance a standard `HttpRequest` instance.

    Kwargs:
        - request(HttpRequest). The original request instance.
        - parsers_classes(list/tuple). The parsers to use for parsing the
          request content.
        - authentication_classes(list/tuple). The authentications used to try
          authenticating the request's user.
    """

    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        assert isinstance(request, HttpRequest), (
            'The `request` argument must be an instance of '
            '`django.http.HttpRequest`, not `{}.{}`.'
            .format(request.__class__.__module__, request.__class__.__name__)
        )

        self._request = request
        self.parsers = parsers or ()
        self.authenticators = authenticators or ()
        self.negotiator = negotiator or self._default_negotiator()
        self.parser_context = parser_context
        self._data = Empty
        self._files = Empty
        self._full_data = Empty
        self._content_type = Empty
        self._stream = Empty

        if self.parser_context is None:
            self.parser_context = {}
        self.parser_context['request'] = self
        self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET

        force_user = getattr(request, '_force_auth_user', None)
        force_token = getattr(request, '_force_auth_token', None)
        if force_user is not None or force_token is not None:
            forced_auth = ForcedAuthentication(force_user, force_token)
            self.authenticators = (forced_auth,)

self._request = request在这里是原生的request对象,被包装到class Request可以用from rest_framwork.request import Request

3.2 原生method ,request.data,request.query_params

视图函数
print(request.method)
return HttpResponse('ok')

当我们打印的时候为什么还能打印出原来request对象的属性呢,这个是点拦截属性, 魔法语法,

点的时候会调用__getattr__ 这个是Request重新写了这个方法

 def __getattr__(self, attr):

        try:
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)

通过反射如果取method这个方法,到self._request去取出来的,如果有异常通过下面的方式取,以后是由request对象就像使用之前的request对象一样的,其实就是通过魔法方法,让你发现跟原来的一样,没有发觉。

print(request.data),这个是方法,@property修饰了

源码:request.data

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

我们我们一直按照_full_data点下去,其实就是一个字典,不管使用了什么方式,什么编码传过来的数据都在request.data

 def post(self,request):
        print(request.data)
        return HttpResponse('OK')
    urlencoded:<QueryDict: {'name': ['aaaa'], 'age': ['111']}>
    json:{'name': 'zzz', 'age': 333}
    

我们发现返回的是一个QueryDict我们导入模块看一下from django.http import QueryDict

class QueryDict(MultiValueDict):
    
     def copy(self):
        """Returns a mutable copy of this object."""
        return self.__deepcopy__({})

它继承了字典,但是不能改,但是它copy一遍在修改

get请求传过来的数据

request.GET这个能取出来,在Request又写了一个
print(request.query_params)在get请求,地址参数里
   @property
    def query_params(self):
        """
        More semantically correct name for request.GET.
        """
        return self._request.GET

这样写是为了符合rest_framwork的规范,文件也是一样重写了

好我们收回,把思路跳到我们查询请求的方式APIView里面的 dispatch

       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

这里的异常处理无论出现什么错误,返回的都是json格式的错误。

posted @ 2020-07-06 21:36  小子,你摊上事了  阅读(194)  评论(0编辑  收藏  举报