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