CBV与APIView源码
CBV源码分析
查看源码的方式,先查看自身,没有去找父类,父类没有就去找父父类。。。
自己定义的类
class Author(View): def get(self,request): back_dic = {'status_code': 100, 'msg': 'get ok'} return JsonResponse(back_dic) def post(self,request): back_dic = {'status_code': 102, 'msg': 'post ok'} return JsonResponse(back_dic)
我们看到自己定义的类中并没有这个as_view()方法,那么他只能是从我们继承的父类View中来的,那我们就点到View里面去看有没有as_view()这个方法
刚好就在这个里面,然后点开这个方法,看里面具体做了什么操作
def as_view(cls, **initkwargs): """ Main entry point for a request-response process. """ for key in initkwargs: if key in cls.http_method_names: raise TypeError("You tried to pass in the %s method name as a " "keyword argument to %s(). Don't do that." % (key, cls.__name__)) if not hasattr(cls, key): raise TypeError("%s() received an invalid keyword %r. as_view " "only accepts arguments that are already " "attributes of the class." % (cls.__name__, key)) def view(request, *args, **kwargs): 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 # take name and docstring from class update_wrapper(view, cls, updated=()) # and possible attributes set by decorators # like csrf_exempt from dispatch update_wrapper(view, cls.dispatch, assigned=()) return view
简单分析,如图
我们看到,调用了as_view()实际上是走了view方法,然后再view方法中给我们返回了一个对象的dispatch方法,那么我们就得搞清楚,现在的self是谁~
self是View的对象,然后我们自定义的类继承了View,所以说self就是我们自己定义类的对象,那么就简单了,我们先去自己定义的类中找我们有没有写dispatch方法,没有再去找父类
父类中dispatch方法源码
def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. 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)
源码分析
此时的handler就是我们发送的请求方式,比如GET,但是是小写的而已,然后再自己定义的类中与get对应上了,所以就会get请求走get,post请求走post。
此处的精髓通过反射取到请求方式🐂🐂🐂
先辈们反射用的太溜了,实在是佩服!
看到这里,发现如果我们想要做一些安全认证,比如说权限之类的话,我们完全可以重写dispatch方法,因为as_view()最后走的不就是dispatch方法么,自己写 了dispatch方法,不就是装饰器的使用么,在不修改源代码和调用方式下加强了功能。
有一个更好用的,别人写好的东西,那就是drf框架
drf的下载
两种方式
cmd下载
pip3 install djangorestframework
pycharm下载
settings中下载/terminalerminal下载
drf的使用
from rest_framework.views import APIView
然后写一个类继承APIView
class MyApiview(APIView): def get(self, request): return HttpResponse('get ok') def post(self, request): return HttpResponse('post ok')
然后我们就可以查看他的源码了
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^book/', views.books), # FBV url(r'^author/', views.Author.as_view()), # View url(r'myapiview', views.MyApiview.as_view()) #APIView ]
源码分析还是我们上面所说的步骤
自身没有就去找父类,父类没有就去找父父类。。。
def as_view(cls, **initkwargs): """ Store the original class on the view function. This allows us to discover information about the view when we do URL reverse lookups. Used for breadcrumb generation. """ if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet): def force_evaluation(): raise RuntimeError( 'Do not evaluate the `.queryset` attribute directly, ' 'as the result will be cached and reused between requests. ' 'Use `.all()` or call `.get_queryset()` instead.' ) cls.queryset._fetch_all = force_evaluation view = super(APIView, cls).as_view(**initkwargs) view.cls = cls view.initkwargs = initkwargs # Note: session based authentication is explicitly CSRF validated, # all other authentication is CSRF exempt. return csrf_exempt(view)
as_view()源码分析
这里返回的是csrf_exempt,说明是以后将不会再校验csrfmiddlewaretoken,然后我们看到了他是调用父类的as_view()方法,再父类中最后走的是对象的dispatch方法,那么现在问题又来了,self是谁?没错,就是我们自己定义的类的对象,发现他没有dispatch方法,就去找他的父类,APIView的dispatch,再走这个之前,我们为了查看方便,要现在设置中show members,如图
然后我们就可以接着找APIView里面是否有dispatch方法,恰好存在
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs 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
源码分析
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs # 这一步就是将原来的request变成了APIView的request request = self.initialize_request(request, *args, **kwargs) # 这里就是将对象自己的request变成了APIView的request方法,此时的request已经是新的request了 self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # Get the appropriate handler method # 这里的request.method.lower()就是新的request的方法,self.http_method_names走的对象的方法,发现这个东西在View中 # 所以最后就是判断新的APIView中的方法是否存在View中 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接收 response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) # 将response重新书写,并赋值给对象 self.response = self.finalize_response(request, response, *args, **kwargs) # 最后将对象的response返回 return self.response
然后我们看一下对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 )
源码分析
Request的源码,因为是类的实例化,所以走的就是__init__,那我们就只看这个
class Request(object): """ 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,)
源码查看
最后,我们发现原来Request类是将原生的request进行了封装,最后返回给我们的是封装后的request方法,查看原生的request只能通过request._request来查看。
所以我们此时在自定义类中的request已经是新的request了,通过这个达到了重写dispatch方法