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