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
CBV as_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)
APIView的as_view()源码

as_view()源码分析

这里返回的是csrf_exempt,说明是以后将不会再校验csrfmiddlewaretoken,具体代码如下

def csrf_exempt(view_func):
    """
    Marks a view function as being exempt from the CSRF view protection.
    """
    # We could just do view_func.csrf_exempt = True, but decorators
    # are nicer if they don't have side-effects, so we return a new
    # function.
    def wrapped_view(*args, **kwargs):
        return view_func(*args, **kwargs)
    wrapped_view.csrf_exempt = True
    return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)

然后我们看到了他是调用父类的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
APIView的diapatch源码

源码分析

    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
View Code

然后我们看一下对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
        )
initialize_request源码

源码分析

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__init__

源码查看

最后,我们发现原来Request类是将原生的request进行了封装,最后返回给我们的是封装后的request方法,查看原生的request只能通过request._request来查看。

所以我们此时在自定义类中的request已经是新的request了,通过这个达到了重写dispatch方法

 

posted @ 2019-07-02 20:47  mcc61  阅读(214)  评论(2编辑  收藏  举报