Django 的视图函数(View)是一个纯粹的 Python 函数,它接收一个 request(HTTP 请求),返回一个 response(HTTP 响应)。在其内部,它主要还负责从数据库中获取数据、处理表单数据、保存数据到数据库、以及渲染指定的 HTML 模板等。我们可以把这些操作逻辑写在一个直观的 Python 函数里,但是 Django 开发者们意识到很多视图函数中的逻辑代码都是重复和通用的,因此在较早的版本中,Django 便开始引入 Class-based View(基于类的视图,这里简称类视图)。
类视图比函数视图提供了更加高层的抽象,它将上边提及的数据库操作、表单处理、模板渲染等通用操作抽取为类视图中的方法,函数的参数、状态等则抽取为类视图的属性,最终通过一个 as_view 方法将整个类视图转换为一个可调用对象(可理解为最终用于 Django URL Pattern 设置中的视图函数)。相比于书写函数视图,在 Django 中使用类视图可使得重复代码更少、代码可复用性更高、代码也更加简洁优雅,但缺点是由于比函数更加高级的抽象层次,理解其代码逻辑更加困难。即使是通读过官方文档的类视图部分,新手在使用过程中依然感到有一定障碍,无法灵活运用各种内置的类通用视图,以及在必要时通过继承的方式拓展类视图。
类视图的好处:
- 代码可读性好
- 类视图相对于函数视图有更高的复用性, 如果其他地方需要用到某个类视图的某个特定逻辑,直接继承该类视图即可
配置路由时,使用类视图的as_view()
方法来添加。
urlpatterns = [
# 视图函数:注册
# url(r'^register/$', views.register, name='register'),
# 类视图:注册
url(r'^register/$', views.RegisterView.as_view(), name='register'),
]
类视图原理
@classonlymethod
def as_view(cls, **initkwargs):
"""
Main entry point for a request-response process.
"""
...省略代码...
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# 调用dispatch方法,按照不同请求方式调用不同请求方法
return self.dispatch(request, *args, **kwargs)
...省略代码...
# 返回真正的函数视图
return view
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)
源码目录结构
Django 类视图的源码位于 django.views.generic 包中,其目录结构如下:
generic/ |—— __init__.py |—— base.py |—— dates.py |—— detail.py |—— edit.py |—— list.py
各个模块中存放的功能代码大致如下:
- base.py 主要存放所有类视图的基类
View
,以及一些和数据库操作无关的类视图如TemplateView
、RedirectView
。 - dates.py 主要存放用于按时间归档的类视图,如
ArchiveIndexView
,一些视图在博客系统中非常有用,例如获取某个日期下的全部文章列表。 - detail.py 主要存放用于从数据库获取单条记录的类视图,例如从数据库中获取某一篇博客文章。
- edit.py 主要包含了表单处理,创建、更新和删除数据库中的单条记录的类视图。
- list.py 主要包含了从数据库中获取多条记录的类视图,例如从数据库中获取全部博客文章列表。
当然这仅仅是一个粗略的概述,后续的系列教程中将详细讲解各个模块中的具体类的作用。
类的继承关系与命名规律
学习 Django 类视图的一个最大障碍在于代码中类的种类繁多,而且继承关系复杂,各种基类和 Mixin,初看之下会让人眼花缭乱。但是类视图的设计者并非随心所欲,随意而为地设计各个类以及为类命名的,设计者充分采纳了一个类只负责一件事的设计理念(即单一责任原则),而且命名也是遵循一套统一的规范(或者可以叫做命名规律)。举一个例子,ListView
主要用于从数据库中获取多条记录,它的继承关系如下:
ContextMixin --> MultipleObjectMixin + | | | | --> BaseListView ---- + | | | View ------------------------------- + | --> ListView | TemplateResponseMixin --> MultipleObjectTemplateResponseMixin +
整个体系非常清晰,各个类的职责也非常明确,且类的职责从命名就可以读出。例如 ContextMixin
及其子类负责获取渲染模板所需的模板变量;MultipleObjectMixin
负责从数据库获取模型对应的多条数据;View
负责处理 HTTP 请求(如 get 请求,post 请求);TemplateResponseMixin
及其子类负责渲染模板。各个类组合在一起就构成了功能完整的 ListView
。
完全类似的,DetailView
也是同一套路:
ContextMixin --> SingleObjectMixin - + | | | | --> BaseDetailView -- + | | | View ------------------------------- + | --> DetailView | TemplateResponseMixin --> SingleObjectTemplateResponseMixin - +
总结起来就是一个类提供一个视图所需的功能,然后将各个类通过多继承的方式组合到一起,就提供了一个功能完整的类视图。