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,可以限制请求

 

posted @ 2021-12-17 16:36  hushowee  阅读(139)  评论(0)    收藏  举报