概述

通常情况下,Django 的视图函数(View)是一个纯粹的 Python 函数,它接收一个 request(HTTP 请求),返回一个 response(HTTP 响应)。在其内部,它主要还负责从数据库中获取数据、处理表单数据、保存数据到数据库、以及渲染指定的 HTML 模板等。我们可以把这些操作逻辑写在一个直观的 Python 函数里,但是 Django 开发者们意识到很多视图函数中的逻辑代码都是重复和通用的,因此在较早的版本中,Django 便开始引入 Class-based View(基于类的视图,这里简称类视图)。

类视图比函数视图提供了更加高层的抽象,它将上边提及的数据库操作、表单处理、模板渲染等通用操作抽取为类视图中的方法,函数的参数、状态等则抽取为类视图的属性,最终通过一个 as_view 方法将整个类视图转换为一个可调用对象(可理解为最终用于 Django URL Pattern 设置中的视图函数)。相比于书写函数视图,在 Django 中使用类视图可使得重复代码更少、代码可复用性更高、代码也更加简洁优雅,但缺点是由于比函数更加高级的抽象层次,理解其代码逻辑更加困难。即使是通读过官方文档的类视图部分,新手在使用过程中依然感到有一定障碍,无法灵活运用各种内置的类通用视图,以及在必要时通过继承的方式拓展类视图(至少对我来说,刚接触类视图时就是这种状态)。

因此,本系列教程将从源码层面解析 Django 类视图的工作原理和设计理念,一旦掌握这些,以后在项目中使用类视图就可以更加得心应手和运用自如。

源码目录结构

Django 类视图的源码位于 django.views.generic 包中,其目录结构如下:

generic/
|—— __init__.py
|—— base.py
|—— dates.py
|—— detail.py
|—— edit.py
|—— list.py

各个模块中存放的功能代码大致如下:

  • base.py 主要存放所有类视图的基类 View ,以及一些和数据库操作无关的类视图如 TemplateViewRedirectView
  • 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 - +

总结起来就是一个类提供一个视图所需的功能,然后将各个类通过多继承的方式组合到一起,就提供了一个功能完整的类视图。

总结

我们从一个很高的层面概览了 Django 类视图的源码结构和类继承体系,建立这样一个宏观的源码结构地图有助于接下来对源码细节的深入探索。接下来我们便要开始深入到各个模块和各个类的源码中一探类视图的究竟了。不过在此之前,如果你还没有读过 Django 关于类视图的文档的话,建议先通读一遍,代码和实例相结合,才能更加直观地理解代码的工作原理。阅读地址:Class-based views

如果遇到问题,请通过下面的方式寻求帮助。

  • 在下方评论区留言。

更多 Django 相关教程,请访问我的个人博客:追梦人物的博客

-- EOF --


10 条评论 / 10 人参与
张旭楠_Python

博主写的非常不错,解了我好多迷惑。我刚刚把旧项目改成基本类的通用视图的方式。博主可以加好友么?


Hu.MFFL

非常详细的教程,解决了很多疑问,受益匪浅。


lm聿喵

xxxx


acutesun
很期待,感谢分享

nevermorever
不错

njcx3
很期待这一系列博客

用户6110090370
哇 最近在学Django 博主一篇文章解决了我大部分迷惑 感谢博主

唯J王道
期待博主后续的教程 受益匪浅!在画BaseListView和BaseDetailView的父类关系的时候是不是画反了 MultipleObjectMixin和SingleObjectMixin 应该画在下面吧 他们是先被继承的类 View应该在上面

追梦人物 [博主] 唯J王道
严格上来说确实是这样。这里只做示例吧。

toSummerDawn
很不错的教材。纯粹的 Python 函数到类视图的进化,解释形象。