• Django 之 CBV


    Django 中有两种编写方式,FBV 和 CBV,那么什么是 FBV,CBV 又是什么呢?

    一、什么是 CBV

    • FBV(function base views) 就是在视图里使用函数处理请求(常见)。
    • CBV(class base views) 就是在视图里使用类处理请求。

    示例:

    1、project/urls.py

    from django.contrib import admin
    from django.urls import path
    from app.views import IndexView
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('index/', IndexView.as_view()),
    ]
    

    2、app/views.py

    from django.shortcuts import render, HttpResponse
    from django.views import View
    
    
    class IndexView(View):
        def get(self, request, *args, **kwargs):
            print('get')
            return HttpResponse('GET')
    
        def post(self, request, *args, **kwargs):
            print('post')
            return HttpResponse('POST')
    

    可以看到所有的请求都是在类 IndexView 中处理的,它继承 View,不管是什么请求,都可以匹配到。

    二、源码分析

    1、CBV 在进行路由匹配时,执行 as_view() 方法,它是类 View 中的一个方法,源码 base.py

    class 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
    

    2、从上面的源码中我们可以看到 as_view(),返回 view() 方法。而 view() 又调用执行 self.dispatch(request, *args, **kwargs) 方法:

    def dispatch(self, request, *args, **kwargs):
    	"""
    	首先要判断请求方法是不是在 self.http_method_names 中(即允许的方法列表中)
    	通过反射,匹配相应方法 get、post、put、delete 等
    	"""
        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)	# 如果匹配上了,就执行它,get(requesr, *args, **kwargs)
    

    http_method_names

    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    

    3、因此大致的执行流程为:

    • 请求过来先执行 URL 中的 as_view() 方法
    • as_view() 返回 view() 方法
    • view() 方法又调用执行 dispatch() 方法
    • dispatch() 中通过反射的方式来匹配相应的请求,是 get 请求,就执行 get() 方法,如果是 post 请求的就执行 post() 方法。

    三、重写 dispatch

    从上面我们指定 CBV 中,请求过来,执行相应视图函数之前,都会先执行 dispatch() 方法。那么如果我们想在处理请求前执行某个方法或者就打印点别的东西,我们可以重写它。

    方法一

    from django.shortcuts import render, HttpResponse
    from django.views import View
    
    
    class IndexView(View):
        def dispatch(self, request, *args, **kwargs):
            print('before...')
            func = getattr(self, request.method.lower())
            ret = func(self, request, *args, **kwargs)
            print('after...')
            return ret
    
        def get(self, request, *args, **kwargs):
            print('get')
            return HttpResponse('GET')
    
        def post(self, request, *args, **kwargs):
            print('post')
            return HttpResponse('POST')
    

    运行结果如下:

    before...
    get
    after...
    

    当有很多个类的时候,不可能每个类都写一个,可以写一个基类,其他类继承基类即可:

    class BasePatch(object):
        def dispatch(self, request, *args, **kwargs):
            print('before...')
            func = getattr(self, request.method.lower())
            ret = func(self, request, *args, **kwargs)
            print('after...')
            return ret
        
    class IndexView(BasePatch, View):
        pass
    

    这样 IndexView 就会先去找基类 BaseView 中的 dispatch() 方法,而不是 View 中的。


    方法二

    也可以继承父类的 dispatch(),不用自己写反射逻辑:

    class BasePatch(object):
        def dispatch(self, request, *args, **kwargs):
            print('before...')
            ret = super(BasePatch, self).dispatch(request,  *args, **kwargs)
            print('after...')
            return ret
    

    四、CSRF

    CBV 中如果想给某个函数免除 csrf_token 认证,可以通过装饰器的形式实现,但是需要注意的是,装饰器必须装饰在类上或者 dispatch 上

    方法一

    from django.utils.decorators import method_decorator
    from django.views.decorators.csrf import csrf_exempt
    
    
    class IndexView(View):
        @method_decorator(csrf_exempt)		# 这句
        def dispatch(self, request, *args, **kwargs):
            print('before...')
            func = getattr(self, request.method.lower())
            ret = func(self, request, *args, **kwargs)
            print('after...')
            return ret
    

    方法二

    装饰在类上,不用 dispatch

    @method_decorator(csrf_exempt, name='dispatch')
    class IndexView(View):
        def get(self, request, *args, **kwargs):
            pass
    
  • 相关阅读:
    15款精美的 WordPress 电子商务网站模板
    15套免费的扁平化界面设计素材【免费下载】
    35幅使用小图片组成的创意插图作品 【上篇】
    sqlserver2014两台不同服务器上数据库同步
    C++ Reflection Library
    美国的包容主要体现在接受移民,那么,中国的包容主要体现在哪里呢?答案就是资本
    mysql主从复制
    Kubernetes+Istio
    Net异步编程
    研发的困境----DEVOPS
  • 原文地址:https://www.cnblogs.com/midworld/p/11380199.html
Copyright © 2020-2023  润新知