• python3-开发进阶Django-CBV和FBV及CBV的源码分析


    一、CBV和FBV

    全称应该是class base views 和function base views
    理解起来应该就是基于类的视图函数和基于函数的视图函数

    FBV

    应该是我目前最常用的一种方式了,就是给每一个views里的功能添加自己专用的方法。例如如果要对网页进行get访问,然后通过获得request中post方式传递的form表单获取数据。

    from django.http import HttpResponse
      
    def login(request):
        if request.method == 'GET':
            return HttpResponse('OK1')
    
        if request.method =="POST":
             return HttpResponse('OK2')
       。。。

    CBV

    但是这种方法,看起来有点臃肿,查看代码的时候不容易看清楚你的post请求get请求是在哪里处理的,所以就有了CBV的处理方法。

    在views文件中:

    from django.views import View
    class LoginView(View):
        def get(self,request):
            return render(request,"login.html")
        def post(self,request):
            user=request.POST.get('user')
            pwd=request.POST.get('pwd'))
            if Turn:  #假使验证成立
                return HttpResponse("OK3")

    在CBV的使用中,需要调用父类View,它会在源码里解释这个CBV的应用范围,以及运作原理。

    在urls文件中:

    from app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^login/$',views.LoginView.as_view()),
      # url(r'^login/$',views.login) ]

    综上所述:
    我们可以知道设置一个CBV方法,要做的就是,在views里创建一个类,这个类的父类一定得是View,而且在urls设置的时候,url指向的不再是一个函数名,而是你定义的类的.as_view()方法

    运行起来之后,会发现当向login.html这个url发送get请求的时候,成功,发送post请求的时候,也成功,并且有相应的返回值。

    二、原理

    翻看源码,实现以上功能的核心,其实都在那个被继承的父类View里:

    class View(object):
        """
        Intentionally simple parent class for all views. Only implements
        dispatch-by-method and simple sanity checking.
        """
    
        http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    
        def __init__(self, **kwargs):
            """
            Constructor. Called in the URLconf; can contain helpful extra
            keyword arguments, and other things.
            """
            # Go through keyword arguments, and either save their values to our
            # instance, or raise an error.
            for key, value in six.iteritems(kwargs):
                setattr(self, key, value)
    
        @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
    
        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())
    
        def options(self, request, *args, **kwargs):
            """
            Handles responding to requests for the OPTIONS HTTP verb.
            """
            response = http.HttpResponse()
            response['Allow'] = ', '.join(self._allowed_methods())
            response['Content-Length'] = '0'
            return response
    
        def _allowed_methods(self):
            return [m.upper() for m in self.http_method_names if hasattr(self, m)]

    从源码中我们可以看到。这个CBV的核心类,是为了处理各种请求服务的。其中有一个list来存放这些请求,并且指向他们应该实现的函数功能。
    再补充一点,在CBV创建类方法的时候,一定要携带一个request参数。而这个参数里面就携带了request.method.lower(),通过反射,CBV函数自然能处理这些method对应的请求。

    三、逻辑过程

    看源码的准则就看,看自己看的懂得代码,应为源码的有些高深的设置,我们先阶段还不需要去了解.

    首先,不管cbv还是fbv,在url中都是用户访问请求,才回去执行相应的视图函数,

    cbv在启动项目的时候,已经在LoginView中执行一段骚操作的得到一个函数名,我们现在就来看看:

    首先我们要明确的一点就是一个类.属性或方法,首先在自己那找,找不到继承的父类找。所以不能直接点。as_view,

    先去类LoginView中找看看有,有没有方法:

    点开View,我们找到as_view方法,发现是类方法,

    前面过程做了什么我们不管,返回值只是返回一个view,调用view函数,我们去看view函数,

    view函数最后返回self.dispacth,这里要注意!!!这个self.dispacth,self是谁,self我们看代码发现是LoginView类,

    还是那个准则,调用属性方法,现在自己那边找,没有再找父类。

    因为LoginView中没有dispath这方法,所以还是执行的父类的dispath方法:

    注意点:还是在调用某个方法时,我们一定要确定是谁去调用这个方法!找方法一定先找自己的,没有再去父类找!

    四、思考自定义的dispatch

    既然在上面我们查看源码的时候已经发现,导向专门的method操作的函数是dispatch,而且每个CBV类的父类都是View,那我能不能在这个dispatch里面做一些定制化操作,

    • 继承父类的dispatch

       def dispatch(self, request, *args, **kwargs):
          obj=super(login,self).dispatch(request, *args, **kwargs)
          return obj
          # 由于父类的dispatch最后返回了一个handle,也就是一个返回值,所以在继承的时候也应该提供一个返回值
    • 装饰化这个dispatch

      def dispatch(self, request, *args, **kwargs):
          print('123‘)
          obj=super(login,self).dispatch(request, *args, **kwargs)
          print('456')
          return obj

      反馈的效果:

      不管是什么方法请求,都必须要打印123,456

    总的来说,这个只是一个简单的示范处理,如果需要对过来的请求做更多的润色,还需要在这个继承动作前后做更多工作。需要知道的是,他和装饰器略微不懂,那就是他可以共享这个dispatch的request,并且对他进行工作

  • 相关阅读:
    Leetcode 538. Convert BST to Greater Tree
    Leetcode 530. Minimum Absolute Difference in BST
    Leetcode 501. Find Mode in Binary Search Tree
    Leetcode 437. Path Sum III
    Leetcode 404. Sum of Left Leaves
    Leetcode 257. Binary Tree Paths
    Leetcode 235. Lowest Common Ancestor of a Binary Search Tree
    Leetcode 226. Invert Binary Tree
    Leetcode 112. Path Sum
    Leetcode 111. Minimum Depth of Binary Tree
  • 原文地址:https://www.cnblogs.com/ManyQian/p/9396063.html
Copyright © 2020-2023  润新知