天天看点

17Django CBV基类View源码解析

前述章节《Django的FBV与CBV模式》中我们讲解了 Django 中编写视图层函数的两种方式,一种是基于函数即 FBV,另外一种是 CBV 即基于类的视图函数。在本节,我们对类视图中所继承的 View 源码进一步分析,帮助大家更好的理解类视图。若以后在项目中使用它就会更加得心应手。

View 定义于 django/views/generic/base.py 文件中,其功能实现主要依赖于三个重要的方法分别如下所示:

  • dispatch
  • as_view
  • http_method_not_allowed

下面我们根据源码依次对这个三个方法进行分析。

1. http_method_not_allowed方法

这个方法返回 HttpResponseNotAllowed(405)响应,属于继承 HttpResponse 的子类,表示当前的请求类型不被支持。它在 View 类中的源码如下所示:

  1. class View:
  2. def http_method_not_allowed(self, request, *args, **kwargs):
  3. logger.warning(
  4. 'Method Not Allowed (%s): %s', request.method, request.path,
  5. extra={'status_code': 405, 'request': request}
  6. )
  7. return HttpResponseNotAllowed(self._allowed_methods())

比如当一个类视图函数之定义了 get 方法,而没有定义 post 方法,那么这个类视图函数接收到 post 的请求的时候,由于找不到相对应的 post 的定义,就会通过另一个方法 dispatch 分发到 http_method_not_allowed 方法中。

2. dispatch方法分析

dispatch 方法根据 HTTP 请求类型调用 View 中的同名函数,实现了请求的分发,源码如下所示:

  1. class View:
  2. def dispatch(self, request, *args, **kwargs):
  3. if request.method.lower() in self.http_method_names:
  4. handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
  5. else:
  6. handler = self.http_method_not_allowed
  7. return handler(request, *args, **kwargs)

http_method_names 定义当前 View可以接受的请求类型:

  1. http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

3. as_view方法分析

纯文本复制      
  1. @classonlymethod
  2. def as_view(cls, **initkwargs):
  3. for key in initkwargs:
  4. if key in cls.http_method_names:
  5. raise TypeError("You tried to pass in the %s method name as a "
  6. "keyword argument to %s(). Don't do that."
  7. % (key, cls.__name__))
  8. if not hasattr(cls, key):
  9. raise TypeError("%s() received an invalid keyword %r. as_view "
  10. "only accepts arguments that are already "
  11. "attributes of the class." % (cls.__name__, key))
  12. def view(request, *args, **kwargs):
  13. #创建View类实例
  14. self = cls(**initkwargs)
  15. if hasattr(self, 'get') and not hasattr(self, 'head'):
  16. self.head = self.get
  17. self.setup(request, *args, **kwargs)
  18. if not hasattr(self, 'request'):
  19. raise AttributeError(
  20. "%s instance has no 'request' attribute. Did you override "
  21. "setup() and forget to call super()?" % cls.__name__
  22. #调用View实例的dispatch方法
  23. return self.dispatch(request, *args, **kwargs)
  24. view.view_class = cls
  25. view.view_initkwargs = initkwargs
  26. update_wrapper(view, cls, updated=())
  27. update_wrapper(view, cls.dispatch, assigned=())
  28. return view