Django-drf-View和APIView源码分析
概述
根据Django的MTV(m:model.py,T:template,V:view视图函数)三层架构,结合路由,就可以写一个有前后端、数据库的小项目。在视图函数里,可以写CBV(class base view)和FBV(function base view)来实现。在仅使用原生Django的项目中,使用FBV较多,在使用drf的项目中,CBV会多一些。但源码里面,都是通过FBV实现的。
本篇文章通过CBV的视角,去分析源码是整套流程。
urls.py
如果使用的是CBV,那么在urls.py中的路由写法为
urlpatterns = [ path('admin/', admin.site.urls), re_path('books/', views.BooksView.as_view()), # 这里是调用了as_view()方法,且views后面一定是一个内存地址 ]
若视图层的类继承的是View,那么简单的写法为
class BooksView(View): # 继承View,如果使用Django写项目,必须要继承 def get(self, request): # request一定要写 return HttpResponse('ok') # 返回HttpResponse类型的数据
所以从as_view()方法这里去分析源码,根据mro或根据经验或简单的根据当前类没有去父类找的查找顺序,先从视图函数定义的CBV的类中找as_view()方法,如果没有,那么就去父类,即View中找。
视图层
View中的as_view()方法
通过按住Ctrl然后鼠标悬浮在View上,点击鼠标左键,就可以跳转到View的源码中,其中,直接看as_view(),
@classonlymethod # 表示这是一个类的绑定方法,通过类来调用,验证了urls.py中的view.xxx.as_view() def as_view(cls, **initkwargs): ... def view(request, *args, **kwargs): # 定义了view方法 self = cls(**initkwargs) # 通过解压赋值,将urls.py中的views.BookView.as_view()中的参数赋值给当前self,即View的实例化的对象,侧面说明as_view()可以传参数 self.setup(request, *args, **kwargs) # 可以在as_view()里面传参 if not hasattr(self, 'request'): # request表示当前请求的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) # 调用了dispatch方法,是整个视图层的核心 ...return view # 返回了view的内存地址
从这个结构来看,这就是典型的闭包函数,定义在函数内部,且使用了外部参数。也类似是一个装饰器。
最后的return view,返回view方法的内存地址,也侧面证明了,urls.py中的views后面是一个内存地址
View中的dispatch()方法
在as_view中,调用了dispatch方法
def dispatch(self, request, *args, **kwargs): if request.method.lower() in self.http_method_names: # 判断请求方法的小写是否在http_method_names中 handler = getattr(self, request.method.lower(), self.http_method_not_allowed) # 条件成立,则使用反射,去对象中获取方法的的内存地址,然后赋值给handler else: handler = self.http_method_not_allowed # 条件不成立,将http_method_not_allowed赋值给handler return handler(request, *args, **kwargs) # 最后返回处理结果
1、先看 http_method_names 有什么
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
http_method_names就是一个列表,定义了http协议中的请求方法,所以我们在写视图函数时,也要与之对应,否则就会出错
2、handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
若前端的请求方式为GET,我们在视图层中的类也写了get方法,那么根据mro原则,找request.method.lower(),先去视图层中定义的视图类找,视图类是有这个属性的,因为在视图类中定义了一个get方法,如果前端的请求方法为get,那么,就会在这里找到。所以handler就是自定义视图类的get方法的内存地址。
3、看 http_method_not_allowed 有什么
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()) # 返回允许的请求方式
4、看_allowed_methods()有什么
def _allowed_methods(self): return [m.upper() for m in self.http_method_names if hasattr(self, m)] # 通过反射和列表生成式,去自定义视图类中将定义的方法的大写返回,比如定义了get,就会返回GET
5、return handler(request, *args, **kwargs)
如果请求方式存在的话,那么handler此时就是我们自己写的方法的内存地址,然后调用该方法,传入参数,根据自己写的逻辑,去处理前端传过来的数据
限制请求
通过分析源码,http_method_names里面设置了允许的请求方式,在视图类中继承了View,我们可以重写这个参数的值,如,只允许get请求
class BookView(views.View):
http_method_names = ['get',] # 重写这个属性
通过http_method_names可以定制仅允许的操作,不过在视图类中没有写对应的请求方式的处理方式的话,侧面的抛出异常的方法限制了请求方式
总结
1、路由中的views后紧着着的是一个内存地址
2、as_view通过闭包函数,定义了view方法,来处理
3、view中的核心是dispatch
4、dispatch通过反射获取视图类中的方法的地址,然后传参,处理前端的请求,所以在视图类中的自己写的方法名要与http_method_names对应
5、通过重写http_method_names,可以限制请求
浙公网安备 33010602011771号