FBV(function base views) 就是在视图里使用函数处理请求。
CBV(class base views) 就是在视图里使用类处理请求。
Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。所以Django在后来加入了Class-Based-View。可以让我们用类写View。这样做的优点主要下面两种:
- 提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
- 可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
CBV源码解析:
首先,在urls中,我们会有这样一个访问函数:
path('login/',views.LoginView.as_view() )
当访问login时,django会调用 views.LoginView.as_view() 中 as_view() 这个函数,那么必须先要去看看LoginView中该函数构成:
class LoginView(View): def get(self,request): return render(request,"login.html") def post(self,request): return HttpResponse("Hello")
LoginView 是我们在视图中创建的类,继承View,LoginView 没有as_view这个方法,于是我们来到继承的父类View,并找到as_view
1 @classonlymethod 2 def as_view(cls, **initkwargs): 3 """Main entry point for a request-response process.""" 4 for key in initkwargs: 5 if key in cls.http_method_names: 6 raise TypeError("You tried to pass in the %s method name as a " 7 "keyword argument to %s(). Don't do that." 8 % (key, cls.__name__)) 9 if not hasattr(cls, key): 10 raise TypeError("%s() received an invalid keyword %r. as_view " 11 "only accepts arguments that are already " 12 "attributes of the class." % (cls.__name__, key)) 13 14 def view(request, *args, **kwargs): 15 self = cls(**initkwargs) 16 if hasattr(self, 'get') and not hasattr(self, 'head'): 17 self.head = self.get 18 self.request = request 19 self.args = args 20 self.kwargs = kwargs 21 return self.dispatch(request, *args, **kwargs) 22 view.view_class = cls 23 view.view_initkwargs = initkwargs 24 25 # take name and docstring from class 26 update_wrapper(view, cls, updated=()) 27 28 # and possible attributes set by decorators 29 # like csrf_exempt from dispatch 30 update_wrapper(view, cls.dispatch, assigned=()) 31 return view
从源码中可以看到as_view最后返回了一个view
此时
path('login/',views.LoginView.as_view() ) ------->>>> path('login/',View.view )
当有用户登录Login页面时,dajango会自动调用 View.view
又找到view的方法,源码如下:
1 def view(request, *args, **kwargs): 2 self = cls(**initkwargs) 3 if hasattr(self, 'get') and not hasattr(self, 'head'): 4 self.head = self.get 5 self.request = request 6 self.args = args 7 self.kwargs = kwargs 8 return self.dispatch(request, *args, **kwargs)
解读一下view方法:
self = cls(**initkwargs) cls为调用as_view时传入的类,为调用该类的对象 LoginView ,这里self为 LoginView 的实例对象.
return self.dispatch(request, *args, **kwargs) 最后返回的时候,先调用了self.dispatch(request, *args, **kwargs)这个方法.而cls就是LoginView ,一步步找到
dispatch这个方法
dispatch源码(分发):
1 def dispatch(self, request, *args, **kwargs): 2 # Try to dispatch to the right method; if a method doesn't exist, 3 # defer to the error handler. Also defer to the error handler if the 4 # request method isn't on the approved list. 5 if request.method.lower() in self.http_method_names: 6 handler = getattr(self, request.method.lower(), self.http_method_not_allowed) 7 else: 8 handler = self.http_method_not_allowed 9 return handler(request, *args, **kwargs)
if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
其中
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
获取反射获取到对应的方法('get', 'post', 'put'....)
最后返回:handler(request, *args, **kwargs) 的调用结果,完成分发
self.http_method_not_allowed 为错误或找不到值时报错调用的方法
更多可以查看