1. 程式人生 > >django的view.view和DRF的APIView的原始碼解析

django的view.view和DRF的APIView的原始碼解析

一.classbasedview的原始碼剖析

1. 啟動django:python manage.py runserver 127.0.0.1:8000
2. 載入settings
  2.1 讀取models.py
  2.2 views.py
  2.3 urls.py
    2.3.1 開始執行as_views(): views.LoginView.as_view(), 返回view函式
    2.3.2 此時url對應具體的某一個函式
  2.4 開始等待使用者請求(127.0.0.1:8000/books/)
  2.5 開始執行view函式:view(request)

urls.py

from
django.urls import path, include, re_path from classbasedview import views urlpatterns = [ re_path('books/$', views.BookView.as_view()), ]

viws.py

from django.views import View

class View:
    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    @classmethod
    
def as_view(cls, **initkwargs): def view(request, *args, **kwargs): # 例項化一個物件,物件名稱為self,self是cls的物件,誰呼叫了cls # cls就是誰(當前呼叫cls的是BookView), # 所以,此時的self就是BookView的例項化物件 self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, '
head'): self.head = self.get # 此時的request物件指向原始的request物件 # 給self這個例項化物件賦值:原始的request self.request = request self.args = args self.kwargs = kwargs # 開始執行self.dispatch() return self.dispatch(request, *args, **kwargs) return view def dispatch(self, request, *args, **kwargs): if request.method.lower() in self.http_method_names: # 通過getattr找到的屬性,已經和物件綁定了,訪問的時候不需要在指明物件了 # 不需要再:self.handler # 直接handler() handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) class BookView(View): def get(self, request): pass def post(self, request): pass

二.DRF的APIView的請求流程和原始碼剖析

  本質上DRF是django的一個app(startproject)

  flask:flask-REST

  封裝了很多功能

    - APIView(所有的功能都是基於APIView的)

    - 解析器元件
    - 序列化元件
    - 認證元件
    - 許可權元件
    - 檢視元件

1.使用方法

1. 匯入模組
    views.py

    from rest_framework.views import APIView
                    
2. 繼承APIView
      class BookView(APIView):
           def get(self, request):
                pass
                            
          def post(self, request):
                pass
                    
3. urls.py
      from django.urls import path, include, re_path
      from classbasedview import views

      urlpatterns = [
            re_path('login/$', views.LoginView.as_view()),
      ]

2.原始碼解析

  1. 啟動django:Python manage.py runserver 127.0.0.1:8000
  2. 載入settings:
    2.1 載入models.py
    2.2 載入views.py
    2.3 載入urls.py
      2.3.1 re_path('BookView/$', views.BookView.as_view()), 開始執行as_view()方法
      2.3.2 urls.py載入完畢,url和檢視函式之間的繫結關係已經建立好了
    3. 等待使用者請求
    4. 接收到使用者請求:127.0.0.0:8000/books/
    5. 開始查詢url和檢視函式之間的繫結關係,根據使用者請求的url找到對應的檢視函式
    6. 開始執行檢視函式view(request)
    7. 開始執行self.dispatch()
    8. 將view函式的返回結果返回給客戶端瀏覽器

class View:
    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

    def __init__(self, **kwargs):

         for key, value in kwargs.items():
             setattr(self, key, value)

     @classonlymethod
     def as_view(cls, **initkwargs):
         def view(request, *args, **kwargs):
              # 例項化一個物件,BookView的例項物件
              self = cls(**initkwargs)
              # self表示BookView的例項化物件
              # 把原始的request請求物件賦值給self.request
              self.request = request
              self.args = args
              self.kwargs = kwargs
              # view函式的返回結果就是self.dispatch()
              return self.dispatch(request, *args, **kwargs)
         # 此時的cls是BookView
         view.view_class = cls
         view.view_initkwargs = initkwargs

         return view

     class APIView(View):
          @classmethod
          def as_view(cls, **initkwargs):
               # cls是BookView

               view = super(APIView, cls).as_view(**initkwargs) # View:view
               view.cls = cls
               view.initkwargs = initkwargs

               # 返回一個view函式
               return csrf_exempt(view)
                    
           def dispatch(self, request, *args, **kwargs):
               try:
                            
                  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)

                return self.response

        class BookView(APIView):
             def get(self, request):
                 pass
                        
             def post(self, request):
                 pass

三.DRF解析器的請求流程和原始碼剖析

1.使用方法

1. 匯入模組
    views.py

    from rest_framework.views import APIView
                    
2. 繼承APIView
    class BookView(APIView):
         def get(self, request):
              pass
                            
         def post(self, request):
              pass
                    
3. urls.py
     from django.urls import path, include, re_path
     from classbasedview import views

     urlpatterns = [
         re_path('login/$', views.LoginView.as_view()),
     ]
                    
4. def post(self, request):
       origin_data = request.data
       ....
       return HttpResponse({})

2.原始碼解析

  1. 啟動django:Python manage.py runserver 127.0.0.1:8000
  2. 載入settings:
    2.1 載入models.py
    2.2 載入views.py
    2.3 載入urls.py
      2.3.1 re_path('BookView/$', views.BookView.as_view()), 開始執行as_view()方法
      2.3.2 urls.py載入完畢,url和檢視函式之間的繫結關係已經建立好了
  3. 等待使用者請求
  4. 接收到使用者請求:127.0.0.0:8000/books/ POST
  5. 開始self.post()
    5.1 request.data觸發解析操作
    5.2 獲取返回值
  6. 將view函式的返回結果返回給客戶端瀏覽器

class View:
    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

    def __init__(self, **kwargs):

         for key, value in kwargs.items():
              setattr(self, key, value)

    @classonlymethod
    def as_view(cls, **initkwargs):
         def view(request, *args, **kwargs):
              # 例項化一個物件,BookView的例項物件
              self = cls(**initkwargs)
              # self表示BookView的例項化物件
              # 把原始的request請求物件賦值給self.request
              self.request = request
              self.args = args
              self.kwargs = kwargs
              # view函式的返回結果就是self.dispatch()
              return self.dispatch(request, *args, **kwargs)
          # 此時的cls是BookView
          view.view_class = cls
          view.view_initkwargs = initkwargs

          return view
                        
     class Request(object):
           def __init__(self, request, parsers=None, authenticators=None,
                   negotiator=None, parser_context=None):
               elf._request = request
                self.parsers = parsers or ()  # self.get_parsers()的執行結果
                        
            # 觸發解析操作
            @property
            def data(self):
                 if not _hasattr(self, '_full_data'):
                      self._load_data_and_files()
                 return self._full_data
                        
            def _load_data_and_files(self):
                 """
                 Parses the request content into `self.data`.
                 """
                 if not _hasattr(self, '_data'):
                      # 開始執行self._parse()
                      self._data, self._files = self._parse()  # parsed_data
                      if self._files:
                           self._full_data = self._data.copy()
                           self._full_data.update(self._files)
                      else:
                           self._full_data = self._data

                      # if a form media type, copy data & files refs to the underlying
                      # http request so that closable objects are handled appropriately.
                      if is_form_media_type(self.content_type):
                          self._request._post = self.POST
                          self._request._files = self.FILES
                                
             def _parse(self):
                  """
                  Parse the request content, returning a two-tuple of (data, files)

                  May raise an `UnsupportedMediaType`, or `ParseError` exception.
                  """
                  media_type = self.content_type

                  parser = self.negotiator.select_parser(self, self.parsers)

                  if not parser:
                      raise exceptions.UnsupportedMediaType(media_type)

                  try:
                       parsed = parser.parse(stream, media_type, self.parser_context)
                  except Exception:
                            # If we get an exception during parsing, fill in empty data and
                            # re-raise.  Ensures we don't simply repeat the error when
                            # attempting to render the browsable renderer response, or when
                            # logging the request or similar.
                            self._data = QueryDict('', encoding=self._request._encoding)
                            self._files = MultiValueDict()
                            self._full_data = self._data
                            raise

                        # Parser classes may return the raw data, or a
                        # DataAndFiles object.  Unpack the result as required.
                        try:
                            return (parsed.data, parsed.files)
                        except AttributeError:
                            empty_files = MultiValueDict()
                            return (parsed, empty_files)
                                

                class APIView(View):
                    from rest_framework.settings import api_settings

                    parser_classes = api_settings.DEFAULT_PARSER_CLASSES
                    
                    @classmethod
                    def as_view(cls, **initkwargs):
                        # cls是BookView

                        view = super(APIView, cls).as_view(**initkwargs) # View:view
                        view.cls = cls
                        view.initkwargs = initkwargs

                        # 返回一個view函式
                        return csrf_exempt(view)
                    
                    def initialize_request(self, request, *args, **kwargs):
                        from rest_framework.request import Request

                        return Request(
                            request,
                            parsers=self.get_parsers(),
                        )
                        
                    def get_parsers(self):
                        # [<class 'rest_framework.parsers.JSONParser'>, 
                        # <class 'rest_framework.parsers.FormParser'>, 
                        # <class 'rest_framework.parsers.MultiPartParser'>]
                        return [parser() for parser in self.parser_classes]
                    
                    def dispatch(self, request, *args, **kwargs):
                        # 初始化request,將原來的request物件傳遞給初始化函式
                        request = self.initialize_request(request, *args, **kwargs)
                        self.request = request
                        try:
                            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)

                        return self.response

                class BookView(APIView):
                    def get(self, request):
                        pass
                        
                    def post(self, request):
                        parsed_data = request.data