美文网首页
CBV的请求流程

CBV的请求流程

作者: 小吉头 | 来源:发表于2020-07-05 08:25 被阅读0次

    什么是CBV

    FBV(function base view)基于函数的视图
    CBV(class base view)基于类的视图

    #urls.py
    from django.conf.urls import url
    from user.views import Index,IndexView
    
    urlpatterns = [
        url(r'^fbv/', Index),
        url(r'^cbv/', IndexView.as_view()),
    ]
    #views.py
    #FBV
    def Index(request):
        if request.method == "GET":
            return HttpResponse("success")
    #CBV
    class IndexView(View):
        def get(self,request):
            return HttpResponse("success")
    

    一般都使用CBV,因为类有继承、封装、多态的特性。

    CBV请求流程分析

    先看下路由,url(r'^cbv/', IndexView.as_view()),主要看下as_view()这个类方法。
    IndexView类中并没有定义,查看父类View中有没有。View类的定义是在from django.views import View
    找到父类的as_view()方法:

     @classonlymethod
        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
    

    这是一个类方法,最终返回了view这个方法,所以CBV和FBV路由中最终都是指向一个方法。
    注意力集中到view(request, *args, **kwargs)方法上:

    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)
    

    因为路由中调用是url(r'^cbv/', IndexView.as_view()),最终调用了父类的as_view()方法中的view()方法,所以该view()方法里的cls指的是调用时的类,即IndexView,self就是IndexView的实例。
    最终返回了self.dispatch(request, *args, **kwargs),先去IndexView类中查看,有没有dispatch()这个实例方法,发现没有,再去父类View中查看,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)
    
        def http_method_not_allowed(self, request, *args, **kwargs):
            logger.warning(
                'Method Not Allowed (%s): %s', request.method, request.path,
                extra={'status_code': 405, 'request': request}
            )
            return http.HttpResponseNotAllowed(self._allowed_methods())
    

    根据方法注释可以知道作用是调用正确的方法,如果方法不存在或者方法不在允许列表都会报错。
    request.method.lower(),request.method返回的是大写的请求方式,比如GET,POST。转换成小写是因为我们写的请求处理方法都是小写。
    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']这是允许的方法列表。
    现在来看下最关键的一句代码:
    handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    作用是获取self这个对象的 request.method.lower()属性,如果没有该属性指定调用self.http_method_not_allowed方法处理异常
    翻译后:
    handler = getattr(self, "get", self.http_method_not_allowed)
    上面说过self是IndexView的示例,会优先在IndexView中寻找get属性或方法,等同于self.get。打印print(type(self.get))得到<class 'method'>,说明可以获取到IndexView中的get()方法的引用。
    所以handler(request, *args, **kwargs)等同于get(request, *args, **kwargs)即执行了IndexView中的get()方法。

    重写父类的dispatch()方法

    重写父类dispatch()方法达到类似中间件的效果,每次请求开始和结束都会打印开始和结束

    class IndexView(View):
    
        def dispatch(self, request, *args, **kwargs):
            print("请求开始")
            ret = super(IndexView, self).dispatch(request, *args, **kwargs)
            print("请求结束")
            return ret
    
        def get(self,request):
            return HttpResponse("success")
    

    重写dispatch(),里面调用的其实还是父类的方法

    将dispatch()方法封装到公共类中

    为了方便其他类也可以使用,进一步封装到一个公共类中

    class Base:
        def dispatch(self, request, *args, **kwargs):
            print("请求开始")
            ret = super(Base, self).dispatch(request, *args, **kwargs)
            print("请求结束")
            return ret
    
    
    class IndexView(Base,View):
    
        def get(self,request):
            return HttpResponse("success")
    

    此时,再按照CBV的流程走一遍,先找到IndexViewas_view()方法,由于是多继承,Base类中没有,去View类中继续找,找到后发现最终调用的self.dispatch()方法。还是那句话,self指的是IndexView实例,因此根据多继承关系,从左到右,在Base类中找到dispatch()方法,开始执行...
    ret = super(Base, self).dispatch(request, *args, **kwargs)
    执行到这一句,要调用super(Base, self).ispatch(),此时该怎么走?
    先看下面的多继承super()调用顺序,再回到这里,执行IndexView.mro()得到[<class 'user.views.IndexView'>, <class 'user.views.Base'>, <class 'django.views.generic.base.View'>, <class 'object'>]Base中的super(Base, self).dispatch(request, *args, **kwargs)会到View方法中去执行dispatch()方法。

    多继承中super()的调用顺序

    参考这篇博客https://blog.csdn.net/u011318077/article/details/89677240
    ,复制了里面三个案例代码:

    demo1

    class Parent(object):
        def __init__(self, name):
            print('parent的init开始被调用')
            self.name = name
            print('parent的init结束被调用')
    
    # 新建属性age,继承属性name
    class Son1(Parent):
        def __init__(self, name, age):
            print('Son1的init开始被调用')
            self.age = age
            # 继承父类中的__init__初始化方法
            Parent.__init__(self, name)
            print('Son1的init结束被调用')
    
    # 新建属性gender,继承属性name
    class Son2(Parent):
        def __init__(self, name, gender):
            print('Son2的init开始被调用')
            self.gender = gender
            # 继承父类中的__init__初始化方法
            Parent.__init__(self, name)
            print('Son2的init结束被调用')
    
    # 继承属性name,age,gender
    class Grandson(Son1, Son2):
        def __init__(self, name, age, gender):
            print('Grandson的init开始被调用')
            # 继承父类的初始化方法
            Son1.__init__(self, name, age)
            Son2.__init__(self, name, gender)
            print('Grandson的init结束被调用')
    
    gs = Grandson('grandson', 12, '男')
    >>>
    Grandson的init开始被调用
    Son1的init开始被调用
    parent的init开始被调用
    parent的init结束被调用
    Son1的init结束被调用
    Son2的init开始被调用
    parent的init开始被调用
    parent的init结束被调用
    Son2的init结束被调用
    Grandson的init结束被调用
    

    Parent的初始化方法会被调用两次

    demo2

    class Parent(object):
        def __init__(self, name, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
            print('parent的init开始被调用')
            self.name = name
            print('parent的init结束被调用')
    
    class Son1(Parent):
        def __init__(self, name, age, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
            print('Son1的init开始被调用')
            self.age = age
            super().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数
            print('Son1的init结束被调用')
    
    class Son2(Parent):
        def __init__(self, name, gender, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
            print('Son2的init开始被调用')
            self.gender = gender
            super().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数
            print('Son2的init结束被调用')
    
    class Grandson(Son1, Son2):
        def __init__(self, name, age, gender):
            print('Grandson的init开始被调用')
            # 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍
            # 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
            # super(Grandson, self).__init__(name, age, gender)
            super().__init__(name, age, gender)
            print('Grandson的init结束被调用')
    
    print(Grandson.mro())
    
    gs = Grandson('grandson', 12, '男')
    >>>
    (<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
    Grandson的init开始被调用
    Son1的init开始被调用
    Son2的init开始被调用
    parent的init开始被调用
    parent的init结束被调用
    Son2的init结束被调用
    Son1的init结束被调用
    Grandson的init结束被调用
    

    多继承使用super(),每个父类都只会被调用一次。Grandson.mro()可以查看多继承中的执行顺序

    demo3

    class Parent(object):
        def __init__(self, name, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
            print('parent的init开始被调用')
            self.name = name
            print('parent的init结束被调用')
    
    class Son1(Parent):
        def __init__(self, name, age, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
            print('Son1的init开始被调用')
            self.age = age
            super().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数
            print('Son1的init结束被调用')
    
    class Son2(Parent):
        def __init__(self, name, gender, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
            print('Son2的init开始被调用')
            self.gender = gender
            super().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数
            print('Son2的init结束被调用')
    
    class Grandson(Son1, Son2):
        def __init__(self, name, age, *args, **kwargs):
            print('Grandson的init开始被调用')
            # 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍
            # 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
            super(Son1, self).__init__(name, age, *args, **kwargs)
            print('Grandson的init结束被调用')
    
    print(Grandson.mro())
    
    gs = Grandson('grandson', 12, '男')
    >>>
    (<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
    Grandson的init开始被调用
    Son2的init开始被调用
    parent的init开始被调用
    parent的init结束被调用
    Son2的init结束被调用
    Grandson的init结束被调用
    

    写成super(Grandson, self).init(name, age, gender),则找Grandson下一个开始执行,与demo2结果一样
    写成super(Son1, self).init(name, age, *args, **kwargs), 则找Son1下一个,从Son2开始执行


    多继承,经典类中搜索顺序是深度优先,新式类中是广度优先。super只在python3中才能使用,super不是直接找父类,而是根据调用者的节点位置(节点表示super(Grandson, self).__init__(name, age, gender)中的self,即Grandson对象)的广度优先顺序来的

    相关文章

      网友评论

          本文标题:CBV的请求流程

          本文链接:https://www.haomeiwen.com/subject/cvwyqktx.html