- 前言
- 目标读者
- 非目标读者
- 本书的结构
- 以实践为基础
- 硬件
- 杂谈:个人的一点看法
- Python 术语表
- Python 版本表
- 排版约定
- 使用代码示例
- 第一部分 序幕
- 第 1 章 Python 数据模型
- 第二部分 数据结构
- 第 2 章 序列构成的数组
- 第 3 章 字典和集合
- 第 4 章 文本和字节序列
- 第三部分 把函数视作对象
- 第 5 章 一等函数
- 第 6 章 使用一等函数实现设计模式
- 第 7 章 函数装饰器和闭包
- 第四部分 面向对象惯用法
- 第 8 章 对象引用、可变性和垃圾回收
- 第 9 章 符合 Python 风格的对象
- 第 10 章 序列的修改、散列和切片
- 第 11 章 接口:从协议到抽象基类
- 第 12 章 继承的优缺点
- 第 13 章 正确重载运算符
- 第五部分 控制流程
- 第 14 章 可迭代的对象、迭代器和生成器
- 14.1 Sentence 类第1版:单词序列
- 14.2 可迭代的对象与迭代器的对比
- 14.3 Sentence 类第2版:典型的迭代器
- 14.4 Sentence 类第3版:生成器函数
- 14.5 Sentence 类第4版:惰性实现
- 14.6 Sentence 类第5版:生成器表达式
- 14.7 何时使用生成器表达式
- 14.8 另一个示例:等差数列生成器
- 14.9 标准库中的生成器函数
- 14.10 Python 3.3 中新出现的句法:yield from
- 14.11 可迭代的归约函数
- 14.12 深入分析 iter 函数
- 14.13 案例分析:在数据库转换工具中使用生成器
- 14.14 把生成器当成协程
- 14.15 本章小结
- 14.16 延伸阅读
- 第 15 章 上下文管理器和 else 块
- 第 16 章 协程
- 第 17 章 使用期物处理并发
- 第 18 章 使用 asyncio 包处理并发
- 第六部分 元编程
- 第 19 章 动态属性和特性
- 第 20 章 属性描述符
- 第 21 章 类元编程
- 结语
- 延伸阅读
- 附录 A 辅助脚本
- Python 术语表
- 作者简介
- 关于封面
12.5 一个现代示例:Django 通用视图中的混入
阅读本节不需要掌握 Django 知识。我只是使用这个框架的一小部分为例说明多重继承的运用,我会尽量给出所需的全部背景知识,而且假设你使用其他语言或框架做过服务器端 Web 开发。
在 Django 中,视图是可调用的对象,它的参数是表示 HTTP 请求的对象,返回值是一个表示 HTTP 响应的对象。我们要关注的是这些响应对象。响应可以是简单的重定向,没有主体内容,也可以是复杂的内容,如在线商店的目录页面,它使用 HTML 模板渲染,列出多个货品,而且有购买按钮和详情页面链接。
起初,Django 提供的是一系列函数,这叫通用视图,实现常见的用例。例如,很多网站都需要展示搜索结果,里面包含很多项目,分成多页,而且各个项目会链接到详细信息页面。在 Django 中,这种需求使用列表视图和详情视图实现,前者用于渲染搜索结果,后者用于生成各个项目的详情页面。
然而,最初的通用视图是函数,不能扩展。如果需求与列表视图相似但不完全一样,那么不得不自己从头实现。
Django 1.3 引入了基于类的视图,而且还通过基类、混入和拿来即用的具体类提供了一些通用视图类。这些基类和混入在 django.views.generic 包的 base 模块里,如图 12-4 所示。在这张图中,位于顶部的两个类,View 和 TemplateResponseMixin,负责完全不同的工作。
在 Classy Class-Based Views 网站中可以深入研究这些类,你可以轻松地浏览各个视图类、查看它们的全部方法(继承的、覆盖的和自己添加的)、查看图表、浏览文档,以及跳转到 GitHub 中的源码。
图 12-4:django.views.generic.base 模块的 UML 类图
View 是所有视图(可能是个抽象基类)的基类,提供核心功能,如 dispatch 方法。这个方法委托具体子类实现的处理方法(handler),如 get、head、post 等,处理不同的 HTTP 动词。10RedirectView 类只继承 View,可以看到,它实现了 get、head、post 等方法。
10Django 程序员知道,as_view 类方法是 View 接口最为重要的部分,不过它与这里讨论的话题无关。
View 的具体子类应该实现处理方法,但它们为什么不在 View 接口中呢?原因是:子类只需实现它们想支持的处理方法。TemplateView 只用于显示内容,因此它只实现了 get 方法。如果把 HTTP POST 请求发给 TemplateView,经继承的 View.dispatch 方法检查,它没有 post 处理方法,因此会返回 HTTP 405 Method Not Allowed(不允许使用的方法)响应。11
11如果深入了解设计模式,你会发现 Django 的分派机制是动态版模板方法模式。之所以说是动态的,是因为 View 类不强制子类实现所有处理方法,而是让 dispatch 方法在运行时检查有没有针对特定请求的具体处理方法。
TemplateResponseMixin 提供的功能只针对需要使用模板的视图。例如,RedirectView 没有主体内容,因此它不需要模板,也就没有继承这个混入。TemplateResponseMixin 为 TemplateView 和 django.views.generic 包中定义的使用模板渲染的其他视图(例如 ListView、DetailView,等等)提供行为。图 12-5 是 django.views.generic.list 模块和部分 base 模块的图解。
图 12-5:django.views.generic.list 模块的 UML 类图;图中属于 base 模块的三个类没有详细说明(参见图 12-4);ListView 类没有方法和属性,它是一个聚合类
对 Django 用户来说,在图 12-5 中,最重要的类是 ListView。这是一个聚合类,不含任何代码(定义体中只有一个文档字符串)。ListView 实例有个 object_list 属性,模板会迭代它显示页面的内容,通常是数据库查询返回的多个对象。生成这个可迭代对象列表的相关功能都由 MultipleObjectMixin 提供。这个混入还提供了复杂的分页逻辑,即在一页中显示部分结果,并提供指向其他页面的链接。
假设你想创建一个使用模板渲染的视图,但是会生成一组 JSON 格式的对象,此时用得到 BaseListView 类。这个类提供了易于使用的扩展点,把 View 和 MultipleObjectMixin 的功能整合在一起,避免了模板机制的开销。
与 Tkinter 相比,Django 基于类的视图 API 是多重继承更好的示例。尤其是,Django 的混入类易于理解:各个混入的目的明确,而且名称的后缀都是 ...Mixin。
Django 用户还没有完全拥抱基于类的视图。很多人确实在使用,但是用法有限,把它们当成黑盒;需要新功能时,很多 Django 程序员依然选择编写单块视图函数,负责处理所有事务,而不尝试重用基视图和混入。
学习基于类的视图和根据应用需求扩展它们确实需要一些时间,不过我觉得这是值得的:基于类的视图能避免大量样板代码,便于重用,还能增进团队交流——例如,为模板和传给模板上下文的变量定义标准的名称。基于类的视图把 Django 视图带到了正轨上。
我们对多重继承和混入类的讨论到此结束。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论