2.CBV和类视图as_view源码解析
一、FBV和CBV
# 视图基于函数开发 FBV: function、base、views # 视图基于类开发 CBV: class 、base 、views #Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。 #Django在后来加入了Class-Based-View。可以让我们用类写View。这样做的优点主要下面两种: #1.提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承) #2.可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性 # fbv和cbv本质上是一样的,一个是基于函数开发,一个是基于类开发 # CBV基于反射实现根据请求方式的不同,执行不同的方法
二、类视图
1.类视图必须继承自View(Django原生视图)
from django.views import View #Django会根据发送过来的请求方式,执行对应的请求 #相当于视图类里面给我们做了一个分发 class Infos(View): #例如客户端发送get请求,则会触发get函数下的内容 def get(self,request,*args,**kwargs) pass #例如客户端发送post请求,则会触发get函数下的内容 def post(self,request,*args,**kwargs) pass #例如客户端发送put请求,则会触发get函数下的内容 def put(self,request,*args,**kwargs) pass #例如客户端发送的delete请求,则会触发get函数下的内容 def delete(self,request,*args,**kwargs) pass ...
2.路由中的视图指向类后面需要加as_view
from . import views urlpatterns = [ path('admin/', admin.site.urls), # 路由地址 / views.类名.as_view path('infos/', views.Infos.as_view()), ]
三、as_view源码解析
as_view()核心流程
1.as_view() 内部定义了 view() 函数。view() 函数对类视图进行初始化,返回并调用了 dispatch() 方法。 2.dispatch() 根据请求类型的不同,调用不同的函数(如 get() 、 post()),并将这些函数的 response 响应结果返回。 3.as_view() 返回了这个 view 函数闭包,供 path() 路由调用 ### 原理:url - > view方法 - > dispatch方法(反射执行其他:GET/POST/DELETE/PUT...) 基于反射实现根据请求方式的不同,执行不同的方法
### 源码 ### ''' as_view() 是View的类方法 进入 as_view() 后循环对传入的参数做简单的校验,避免传入的参数将类自己的关键函数名覆盖掉或者传入类中没定义的属性 as_view() 内部又定义了一个 view() 函数, view()首先实例化了类自己 cls(),并赋值给 self ,也就是你编写的类视图的实例。 接着调用 self.setup() 对实例的属性进行了初始化。setup() 方法把接收的参数原封不动的赋值到类实例中。 view() 函数最后返回了 dispatch() dispatch() 非常简短,功能却非常重要:如果 request.method 是一个 GET 请求,则调用类视图 self.get() 方法,如果是 POST 请求,那就调用 self.post() 方法。这就起到根据 http 请求类型派发到不同函数的功能 回到 as_view() 来,它最后做了属性赋值、修改函数签名等收尾工作后,返回了 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( '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 def setup(self, request, *args, **kwargs): """Initialize attributes shared by all view methods.""" if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = 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) 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 HttpResponseNotAllowed(self._allowed_methods())
风月都好看,人间也浪漫.