为什么要用类视图
Django使用函数视图可以完成所有的业务开发,但是维护困难,更不利于扩展,通过继承和复用构建自己的视图并且复用代码,这就是类视图产生的原因,尤其是python支持多继承,可组合继承多个类,极大方便于扩展。
以函数的方式定义的视图称为函数视图(function base view FBV),函数视图便于理解。但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳。
类视图(class base view CBV)
类视图的优点:代码可读性好、代码的复用性更高,如果其他地方需要用到类视图的某个特定逻辑,直接继承该类视图即可,而且可以重写父类的方法实现满足自己的业务需求。
类视图路由分发原理
路由配置
urlpatterns = [ # 类视图(CBV) path('books/', views_class.BooksView.as_view()) ]
http request请求到达路由之后,会交给views_class.BooksView.as_view()方法处理,看下这个方法的源码
class View: """ 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 kwargs.items(): 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( 'The method name %s is not accepted as a keyword argument ' 'to %s().' % (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) self.setup(request, *args, **kwargs) if not hasattr(self, 'request'): raise AttributeError( "%s instance has no 'request' attribute. Did you override " "setup() and forget to call super()?" % cls.__name__ ) 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
可以看到as_view()方法最后返回了view方法的引用,也就是返回了view方法,在view方法里调用self.dispatch(request, *args, **kwargs)方法,进入该方法看下它的实现
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)
如果请求方法名小写在self.http_method_names列表(八种http请求类型)里,那么拿到视图类中方法名==请求方法名小写的方法赋予handler,也就是说如果是get请求,该请求会被视图中名称为get方法拦截并处理。