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
fromdjango.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'] @classmethoddef 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