当前位置: 首页 > 新闻 > 信息荟萃
编号:3623
Python Web开发实战.pdf
http://www.100md.com 2020年3月8日
第1页
第4页
第11页
第30页
第48页
第696页

    参见附件(8400KB,838页)。

     Python Web开发实战,这是一本帮助读者们详细解析Python Web开发的教程书籍,书中涉及了Web框架、Ajax的前后端交互、数据库、数据分析、服务化、部署、系统管理等内容。

    介绍

    《Python Web开发实战》按照一个Web 产品从无到有、从简单变复杂、从基础到进阶的过程,多角度、全方位讲述了Python Web 开发。内容涉及Web 框架、测试、数据库、消息队列、服务化、持续集成等,把网站工程的全貌展现在读者的眼前,从其中可以了解Web 工程从开发到上线的完整流程。另外,作者对当前现在正在流行的技术或工具,如Flask、Celery、Jupyter、Supervisor、SaltStack、Pandas 等都有较为详细的阐述,可作为技术选型时的参考。

    对于Web 开发者、使用Python 语言的运维工程师和运维开发工程师、想提高Python 技能的开发者、想了解Python Web 开发的其他开发者,《Python Web开发实战》都适合阅读。

    作者

    董伟明,豆瓣高级产品开发工程师,主要负责豆瓣读书、电影、音乐、东西等产品线。从2011年开始接触Python, 从运维、运维开发到现在的Web开发,积累了丰富的运维和开发经验,作者积极参与开源项目,给IPython、pip及Python标准库等贡献过代码。这本书将作者这些年使用Python进行Web开发,对各方面知识的理解和积累的经验进行梳理和总结。

    主目录

    第1章 初识Python Web开发

    第2章 Web开发前的准备

    第3章 Flask Web开发

    第4章 Flask开发进阶

    第5章 REST和Ajax

    第6章 网站架构

    第7章 系统管理

    第8章 测试和持续集成

    第9章 消息队列和Celery

    第10章 服务化

    第11章 数据处理

    第12章 帮助工具

    第13章 Python并发编程

    第14章 Python进阶

    第15章 Web开发项目实践

    书评

    1、伟明把他个人多年Web开发的经验,以及豆瓣十年来数百名优秀工程师在Web开发上实践的积累,凝聚在了这本书里,多维度、全面地介绍了Python Web开发涉及的各种技术。我向所有有兴趣使用Python做Web开发的开发者们,强烈推荐此书。

    2、Web开发本身就是一件很庞杂的事情,模版渲染、API的开发、后端的部署,能在一本书中把这些问题都说清楚并不容易。感谢伟明把豆瓣的一些工程实践进行了整理和总结,这是本书宝贵的一点。

    3、整本书都是作者对实际Web项目中大量实战经验的总结,绝非纸上谈兵。相信通过阅读该书可以帮助开发者规避掉大量项目中的“坑”,构建出更高性能、更稳定的Web项目。

    Python Web开发实战截图

    内容简介

    本书按照一个Web产品从无到有、从简单变复杂、从基础到进阶的

    过程,多角度、全方位讲述了Python Web开发。内容涉及Web框架、测

    试、数据库、消息队列、服务化、持续集成等,把网站工程的全貌展现

    在读者的眼前,从其中可以了解Web工程从开发到上线的完整流程。另

    外,作者对当前现在正在流行的技术或工具,如Flask、Celery、Jupyter、Supervisor、SaltStack、Pandas等都有较为详细的阐述,可作为

    技术选型时的参考。

    对于Web开发者、使用Python语言的运维工程师和运维开发工程

    师、想提高Python技能的开发者、想了解Python Web开发的其他开发

    者,本书都适合阅读。未经许可,不得以任何方式复制或抄袭本书之部分或全部内容。

    版权所有,侵权必究。

    图书在版编目(CIP)数据

    Python Web开发实战董伟明著.—北京:电子工业出版社,2016.9

    ISBN 978-7-121-29733-5

    Ⅰ.①P… Ⅱ. ①董… Ⅲ. ①程序开发工具-程序设计 Ⅳ.

    ①TP311.561

    中国版本图书馆CIP数据核字(2016)第200131号

    责任编辑:许 艳

    印 刷:

    装 订:

    出版发行:电子工业出版社

    北京市海淀区万寿路173信箱 邮编:100036

    开 本:787×980 116 印张:31.5 字数:616.9千字

    版 次:2016年9月第1版

    印 次:2016年9月第1次印刷

    定 价:105.00元凡所购买电子工业出版社图书有缺损问题,请向购买书店调换。若书店

    售缺,请与本社发行部联系,联系及邮购电话:(010)88254888,88258888。

    质量投诉请发邮件至zlts@phei.com.cn,盗版侵权举报请发邮件至

    dbqq@phei.com.cn。

    本书咨询联系方式:(010)51260888-819 faq@phei.com.cn。推荐序一

    作为一名使用Python作为主力开发语言十多年的老码农,常常被人

    要求推荐Python相关的图书。经常推荐的都是一些讲解Python语言本身

    的图书,而专注在Python的常见应用领域——Web开发上的好书,却一

    直是凤毛麟角。也曾有出版社的朋友约我写一本,但我畏惧写书的艰

    辛,一直不敢答应。得知伟明的《Python Web开发实战》一书即将出

    版,欣慰异常,心想伟明写这个专题实在是再合适不过,必为佳作。读

    完书稿,果不其然。

    由于Python具有开发快速、适合多人协作、库丰富、社区成熟等优

    点,因此是一门非常适合用于Web开发的语言。国外的Youtube、Quora

    ,国内的豆瓣、知乎等,均是以Python为主要语言开发的。说起Python

    的Web开发,很多人会理解成只要学会某个Web框架,能写代码查查数

    据库,写模板渲染出HTML ,最多再学一下配个Web Server把服务启动

    起来就行,没什么内容。多数Python书里“Web开发”的章节一般也就是

    讲这些。但其实要完成生产可用的,能够应对一定规模访问量的Web系

    统,Web开发工程师要学会的远远不止这些。环境搭建、API设计、网

    站架构、系统管理、持续集成、服务化、数据处理、并发处理等,这些

    都是必要的,而且需要付出大量的努力才能掌握的知识。

    伟明一直对技术抱有极强的兴趣,也有着优秀的动手能力。我对他

    的第一印象是从他发给豆瓣的求职信开始的:“目前我给glances、Salt、tmux-powerline、supervisor、mongo-python-driver、circus、graphite-

    web、Diamond、autopep8、graph-explorer、pip、Celery等开源项目贡献

    过代码,给Python标准库logging贡献过代码。”能够参与到这么多一线开源软件中的应聘者,确实少见。入职豆瓣后,伟明也表现出了对Web

    开发的深刻理解,很快成为豆瓣多个Web产品的主力,并几乎凭一人之

    力完成了CODE项目的私有依赖剥离和开源。

    伟明把他个人多年Web开发的经验,以及豆瓣十年来数百名优秀工

    程师在Web开发上最佳实践的积累,凝聚在了《Python Web开发实战》

    这本书里,多维度、全面地介绍了Python Web开发涉及的各种技术。更

    难能可贵的是,他还在这本书里留下了非常多关于这些技术的思索:为

    什么要使用某个技术?某个需求都有哪些技术可以满足?如何取舍?这

    种不仅要知其然,还要知其所以然的态度,是工程师快速成长必备的。

    这样的一本书能够出版,对于国内的Python开发者绝对是个福音。

    我向所有有兴趣使用Python做Web开发的开发者们,强烈推荐此书。

    洪强宁

    爱因互动CTO

    前豆瓣首席架构师

    前宜信大数据创新中心首席架构师推荐序二

    这篇序酝酿了好几天,今天终于动笔写下了第一个字。说实话,很

    久没有看到关于Python的好书了,尤其是国人自己的原创书。Web开发

    本身就是一件很庞杂的事情,模板渲染、API的开发、后端的部署,能

    在一本书中把这些问题都说清楚并不容易。作者伟明与我都出身豆瓣,虽然没有同时期在豆瓣工作过,但豆瓣的CODE系统把我们俩联系到了

    一起。他是CODE的三代目,通过CODE里一行行Python代码,仿佛知

    道了彼此的心意。感谢伟明把豆瓣的一些工程实践进行了整理和总结,这是本书最宝贵的一点。而本书的精华在于他对各种技术使用场景的探

    讨:那些库谁都会用,但在什么场景使用,在生产环境中这个库的表现

    到底如何,则不见得有认真的思考。期望将来还可以看到越来越多这样

    的图书,祝此书大卖。

    清风

    SAY CEO

    前豆瓣技术总监推荐序三

    一次真诚的倾诉

    自从CPyUG列表订阅人数超过10,000以后,我就认为中文的Python

    学习资料足够多了,而最正确的自学姿势应该从官方文档开始。但是,《Python Web开发实战》一书又改变了我这一偏见。

    有道是:“出版是最好的记忆”,伟明亲身证实了这一点。作为一名

    普通的程序员,只从一个念头出发,独自写出了500多页的技术图书,这实在是一件令人敬佩的事。凡是写过书,特别是写过技术图书的人都

    知道——写书,难的不是写什么,如何写,而是要耐得住寂寞!在中国

    生活原本就如此艰辛,无论上学还是工作,周围永远有无数同侪在竞

    争;而写书几乎是负收入的经济行为,特别是技术图书。当然,图书印

    刷出来,奉上对家人的感谢,是一种别样的程序员式的浪漫情怀,这种

    成就感不足为外人道矣。

    伟明打动我,让我为他写推荐序,就在于他说自己写书的那个简单

    的初衷:让公司所有人都知道Web开发究竟是什么样的,从而能更好地

    协同。这其实已经是佛陀流传经文普度众生的大情怀了!

    Python是如此活跃的一种语言,几乎伴随互联网的发生而创立,又

    伴随互联网的极速发展而繁荣,在Web开发领域拥有全栈式的技术生

    态;又因为脚本语言以及其自身语言的人性化设计,通过Web勾联到了

    几乎所有计算机应用领域,这也导致在特定问题上,Python总是有一堆

    解决方案可以选择,而不像其他语言,只有一种方案。但是选择过多,其实也导致了学习成本的增加。

    伟明将自身在Web领域所有方面的经验提炼后整理成书,本质上是

    将几十个关联产品的官方文档,结合具体工程经验进行了梳理,给出了

    领域问题最佳方案的关键思考点和自己的答案,而更加可贵的是,给出

    了这些思考点的来源,以及形成过程,即给出了解决各种Web领域问题

    的思维模式。

    从前后几个版本的书稿也可以看出,如果没有这本图书的艰苦整

    理,伟明自己也难以形成这种宏观+微观能同时作用的思考模式。所

    以,我一直认为:“输出是更加残酷的输入。”要将纷繁零散的经验,变

    成他人可以习得的技能,要组织成叙述合理、案例得当、结构清晰的图

    书,这个过程本身就得对自己的所有积累进行反复的再学习、解析和增

    补。其中的工作量远远不止这几百页书的内容。

    更加奇妙的是,在没有这部书稿前,其实我们并不熟悉,只是在社

    区列表中见过邮箱名而已。但是,有了独有的知识成果后,伟明就有了

    立场,也有了动机和理由,邀请我以及类似洪教授Limodou这些中国

    Python学习者的前辈来评点和审核书稿,获得直接的联系,即人脉。

    所以,我在郑重推荐此书的内容之外,更加倡议大家向伟明学习

    ——敢于写书,通过真诚的技术图书总结自己的过去,获得更好的未

    来,帮助更多的Pythonista。

    Zoom.Quiet(大妈)

    优视眼动科技CTO

    Python中文社区创始人之一及管理员OBP及蟒营工程设计者兼主持人

    Zoom.Quiet(大妈)

    从2002年开始接触Python,积极推广Pythonic,筹办了自2012年

    起连续四届PyCon中国大会,编撰有《可爱的Python》等图书。作为

    大家熟知的社区“大妈”,主持了OSTC 2015“程序媛专场”,坐实

    了这一称号,得到广大程序员认可。推荐序四

    说起来给《Python Web开发实战》一书写序还真是很突然。2016年

    5月30日,我突然被拉到了一个微信群里,正觉得纳闷的时候,看到群

    里Zoom.Quiet的介绍,才知道是怎么回事。原来《Python Web开发实

    战》已经基本成书,让大家看一看。对于本书的作者董伟明,我们没有

    在线下交流过,但是对Python的热爱时不时地会把大家通过某种方式吸

    引到一起。这是一本原创图书,从书名来看是和Web相关的,而Web领

    域正好和我的兴趣以及平时的工作相关。作为一个开源Web框架的开发

    者,自然对Web开发的内容比较感兴趣,借由此书正好可以了解一下别

    人是如何理解以及如何实践Web开发的,更何况作者还是豆瓣的工程

    师,因此对书的内容还是有一些期待。

    经过一番阅读之后,我与伟明交流了一些看法,他给予了详细的解

    释与说明,我对他的写作思路也有了一些了解。对Web开发的理解其实

    可以有很多角度,比如,从开发者的角度,这就会更多从具体的功能实

    现、框架使用来看待;从运维者的角度,会更多地从部署、维护、平台

    的角度来理解;从测试及质量的角度,会关心代码的测试性及代码审

    查;从框架开发者的角度,就要了解Web开发涉及哪些领域,每一领域

    应该用什么技术与工具来组织,不同领域又如何通过某些框架来有机地

    结合在一起。仅凭一本书,想完全满足所有人的需求是非常困难的。

    阅读本书,我最大的感受就是:全和新。

    全指的是内容覆盖面较广。原本我以为作者会主要讲Flask框架的开

    发,但其实Flask框架在本书中的比重并不大,反而是与Web相关的开发

    技术的介绍占了大部分的篇幅,甚至也包含了部署以及Python本身的一些特性和工具。对此我也有疑问,并向作者咨询。Web开发的概念其实

    太大了,不同的角度可以有不同的理解。比如我们常说的Web框架,其

    实绝大部分都只涉及展示相关的开发,所以应该更精确地称之为Web展

    示框架或Web应用框架。但是它很有可能依赖底层的批处理、大数据处

    理等技术,这些虽然不能算纯粹的Web技术,但是却可以放在Web开发

    这一概念下。因此如果把每一块与Web开发相关的内容都写出来,那么

    本书的厚度就可想而知了。所以作者是从个人实践的角度出发,把他所

    理解的与Web开发相关的技术尽可能全地,并且尽可能用更多的实例来

    讲述。之所以我会有“全”这个感受,因为书的内容涉及了Web框架、Ajax的前后端交互、测试、数据库、数据分析、服务化、部署、系统管

    理、常用工具等内容,有点百科全书的意味。

    为什么说“新”,因为书中讲的许多东西都是现在正在流行的技术或

    工具,像Flask、Celery、Jupyter、Supervisor、SaltStack、Pandas等。其

    中有些我还是第一次接触,说明作者平时接触的内容的确非常丰富,同

    时也结合了豆瓣的一些具体的实例,这样会更有借鉴意义。

    全书的难度不是很大,内容广泛全面,不过因为篇幅所限,对于前

    端的技术介绍得不多,有些章节可能描述也不是太细。不过前端技术虽

    然也算是Web开发技术,但是与Python的关系就不那么紧密了,本书毕

    竟是一本Python相关的书,所以涉及不多也是正常的。而且许多具体的

    技术本身内容都很丰富,也绝不是短篇幅可以说清楚的,所以反而有个

    基础性的介绍,在需要时自行学习可能更好。因此本书比较适合对于

    Web开发有一定了解,但是希望了解更多Python Web开发技术的读者。

    非常感谢作者把自己的经验分享给大家。

    李迎辉Python开源资深行者

    Python-CN邮件列表创建人

    UliPad和Uliweb作者业界热评

    本书从Python开发开始,循序渐进,把网站工程的全貌展现在读者

    的眼前,是了解Web工程从开发到上线完整流程的绝佳参考书籍。同时

    书中的很多实例取自豆瓣工程开发团队的实际工作,对于想了解豆瓣内

    部技术实现的朋友,也有很大的参考价值。

    邢犇(CNBorn)

    前豆瓣东西技术负责人

    开卷有益,已经很久没有看到原创的有价值的Python Web开发书籍

    了。很多刚进入Python世界的人,想要在Web开发上有更多的发展,但

    却不知如何往下学习。伟明的这本书提供了一个非常好的“知识地图”,书中涉及了Python Web开发的方方面面。与此同时,对于那些已经在

    Web开发上积累了一些经验,想要更进一步学习的人来说,这本书也能

    让你收获满满。我阅读完书稿也有了不少收获。书中涉及的知识点非常

    多,任何一个点都可以单独写成一本书。作者根据自己的经验积累,提

    炼出干货,略去了基础的部分,这对于读者来说也是幸事,不然你可能

    得抱一个大部头的书回去了。最后需要说的是,在Web开发的道路上,这本书是不错的进阶指南。

    胡阳(the5fire)

    Python程序员

    目前就职于手机搜狐网任资深开发工程师

    负责m.sohu.com网站的前后端开发和维护

    董伟明是我见过的实践和执行能力超群的工程师。这本书从开发环

    境的搭建,Web框架的使用,到最后的持续集成和Python的进阶用法,无一不是他多年的实际工程经验总结,十分宝贵。如果你刚开始学习

    Python ,这本书能给你展示Python的方方面面,让你可以快速进入实际

    的Web工程的开发。如果你已经使用Python多年,这本书也能让你学习

    到Python的很多使用技巧。

    姚钢强(acmerfight)

    知乎工程师

    这本书非常全面地介绍了使用Python进行Web开发的方方面面,既

    有Web框架、缓存、消息队列、并发处理的场景介绍和技术选型,又有

    开发流程、质量保证的丰富实战经验。作者通过非常细致的Step by Step

    教程,一步一步揭开了Web开发的神秘面纱,不管你有没有Web开发基

    础,相信都能从这本书中获益良多。

    蔡斌(VeryCB)

    DeepDevelop工程师

    前豆瓣条目组技术负责人

    本书适合有一定Python和Web开发基础的用户。书中没有对语言基

    础的讲解,更多的是对Web方面的专注。内容很丰富,基本上覆盖日常

    Web项目开发中涉及的各个层级,对相关概念和原理的描述十分详尽,而每个示例代码都进行了分段解释,清晰明了。

    正如书名,整本书都是作者对实际Web项目中大量实战经验的总

    结,绝非纸上谈兵。相信通过阅读该书可以帮助开发者规避掉大量项目

    中的“坑”,构建出更高性能、更稳定的Web项目。

    强烈推荐从事Web开发的Pythoner阅读。

    Spawnris

    腾讯工程师前言

    为什么写这本书

    2011年,我还在一家互联网商务公司做一枚小小的运维工程师,那

    时公司的运维使用的语言主要是Shell。我本来是一个网络运维,后来在

    工作中开始接触Shell,学了2个月之后,感觉可以应付各种需求,虽然

    程序运行得很慢,但作为一个工作不久且不是IT相关专业毕业的运维人

    员,我还是有点沾沾自喜。这种情况只持续了2个多月,由于公司高速

    发展,一个新入职的同事打破了我的美梦。

    这位新同事入职的第一件工作就是对接各个业务部门的日志需求。

    很快就发生了一件让我特别震撼的事情:同样的一个日志需求,我使尽

    浑身解数用20行Shell写好,运行一次要20分钟,而这位同事使用Perl语

    言的脚本只用了4行,运行3分钟就完成了。可以想象我当时的感受。这

    是我第一次了解到选择正确的工具和方法的重要性。我抑制不住地告诉

    当时的领导悦秋:我要学一门运维使用的高级语言!

    悦秋特别郑重地告诉我:一定要学Python。而在2011年,Python还

    只是一门小众语言,在BAT等大公司招聘时仅作为一些职位的附属要

    求。回想至此,非常感谢悦秋让我选择了正确的路,否则我现在可能只

    能写关于Perl语言实践方面的书了。

    从运维到运维开发,再到豆瓣做产品开发(也就是Web开发),一

    路走来我发现,自己走了很多弯路,没有人告诉我什么是对的,什么是

    错的,该怎么做选择。这些都得自己花时间去琢磨和验证,有时候从Google、StackOverflow搜索答案,或者在GitHub直接看源码获得灵感甚

    至正确答案,而涉及职业规划、该学什么、怎么学,这样的问题除了悟

    性大部分就是靠直觉了。

    从买书看基本语法和拷贝别人的代码开始学习Python,很早我就开

    始努力让代码符合PEP8,尽量让代码写得Pythonic(这点很关键,未来

    就不再需要费力改正学习过程中留下的坏习惯了)。能用Python完成日

    常工作之后,我开始研究和寻找各种Python高级玩法、黑魔法。这个时

    候我还是在不断买书、看书、看之前买的书、看一些技术博客来巩固和

    补充自己的知识体系。所谓“技术”中的“术”也就到这里了。

    有了“术”还远远不够,还需要有实际的经验,以及在正确的时机使

    用正确的工具和方法,这是“技”。“技”是一套分析并解决问题的思路,要想提高“技”,除了个人的领悟,最重要的是靠大量的实践,有时候我

    们称之为“造轮子”。关键是在造的过程中得思考,比如什么时候该抽象

    了?这个轮子和竞品相比有什么优势?技术选型上为什么要使用XX?

    使用Python会遇到这样的问题:什么时候该用多进程?怎样提高代

    码执行效率?Flask为什么流行?曾经遇到一个冷门的Celery的Bug,当

    时使用谷歌没有找到解决方案,甚至解决的思路也没有,怎么办?我开

    始翻阅PEP文档,阅读优秀开源项目的源码,还把Python标准库模块中

    的代码全部过了一遍,收获颇丰。同时我还会根据工作中遇到的问题,给开源项目和Python提一些Issue,后来还给它们提交Patch,用自己微薄

    的能力,让社区变得好一点点。

    虽然过了而立之年,我还在不间断地更新博客(dongwm.com),希望给其他开发者带来帮助和灵感。当许艳编辑找到我时,双方一聊,发现对国内开发者而言实战类Python Web开发方面的书确实不多,我顿时觉得可以以自己多年的工作经验积累写一本,为女儿两岁生日送上一

    份不一样的礼物。作为一个做过运维,现在做后端,却经常写前端程序

    的人来说,我了解产品的整个过程,是适合写一本这样的书的。写这本

    书的意义还在于,将自己这几年在使用Python进行Web开发中对各方面

    知识的理解和积累的经验进行梳理和总结,让更多人受益,同时对自己

    也是一种成长,也算是对国内的Python环境做出个人的贡献了。谁应该看本书

    虽然语言只是工具,但是阅读本书还是需要有一定的Python基础,如果你还没学过Python,那就先学习一段时间再来阅读本书,收获会更

    多。

    本书主要面向如下4类人群:

    Web开发者。

    使用Python语言的运维工程师和运维开发工程师。

    想提高Python技能的开发者。

    想了解Python Web开发的其他开发者。为什么值得看

    本人阅读过大量和Python有关的纸质书和开源图书,渐渐学到了很

    多控制自己“剁手”买书的方法。我来分析一下为什么你值得拥有本

    书。:)

    为什么要买书来看?我认为不外乎两个原因:有趣和能学到东西。

    技术书肯定不会太有趣,那么最重要的就是能学到东西。市面上Python

    相关的书相当多,但是有些内容陈旧或者不符合国情,或者并非开发第

    一线的人所写或者翻译,这样的书显然价值就要打一些折扣;其次是同

    质化严重,偏入门级别,我个人认为市面上关于Python入门或者教授语

    法知识的书不少,而再深入一点的就很匮乏了。

    本书有几个特点:第一,使用了当前主流和前瞻性的技术,如

    Docker、Ubuntu 16.04 LTS、Cython、CFFI、Py.test、asyncio、IPython

    5.0 LTS等,书中一部分内容是在Python 3下完成的。本书中全部工具都

    使用当前最新版,能保证在相当长的时间内书中的内容都不会过时;第

    二,笔者在国内应用Python最大的豆瓣网做产品开发,一直在第一线写

    代码,大量例子和经验都来自实际工作;第三,笔者非常关注GitHub和

    Python社区,会第一时间了解到新的趋势和思想,并在书中体现。举个

    例子,代码检查工具pep8已经在Guido van Rossum的要求下改名为

    pycodestyle了。

    叔本华在《人生的智慧》中说过一段话,大意是“人要么庸俗,要

    么孤独”。笔者认为这个道理在阅读上面也成立:读什么样的书,就会

    逐渐成为什么样的人。本书提供了很多笔者在其他书中没有看到过的思考方式和Python的用法,这也是本书存在的意义。本书的特别用法

    本书中有些效果可能会让读者迷惑,在这里解释一下。

    1.交互模式下的效果。本书在交互模式下完成的例子都使用了

    IPython,效果如下:

    In:template.render(name='Xiao Ming')

    Out:u'Hello Xiao Ming!'

    其中In:是输入,Out:是输出。

    2.终端提示符。本书绝大多数的终端提示符没有使用或者,而

    使用了> 。本书的组织方式和阅读建议

    笔者和身边的一些朋友交流过,大多数人买书来看,基本上都是看

    到书中讲到了自己一直不太懂的知识点,或者感兴趣的话题。因此,在

    写作本书时,笔者有意让每章相对独立。你可以选择跳着看,当然更推

    荐从第1章一直看到最后一章,因为本书是按照一个Web产品从无到

    有、从简单变复杂、从基础到进阶的过程编排的。

    我们先来大致了解一下这个过程。

    第1章 首先回答两个问题:“为什么应该选择Python作为Web开发

    语言”和“选择Python 2还是Python 3”,然后介绍Python主流的Web

    框架,并为如何选择给出建议。

    第2章 帮助读者跑起来一个包含本书所讲内容的Ubuntu环境,读

    者可以直接在里面运行书中的例子。限于篇幅,如果想要了解环境

    搭建的整个过程以及笔者做这些选择的理由,可以在本书源代码项

    目中的setup.md文件中获取,短链接地址为http:bit.ly29N4Pqv。接

    着将展开介绍Python的包管理和虚拟环境相关的内容。通过学习这

    章,读者对Python生态环境会有一定了解。

    第3章 先从最简单的Flask例子开始,学习一些Flask相关的知识,接着学习Jinja2和Mako模板(Mako在豆瓣的使用非常广泛),使用

    MySQL,最后学以致用,从零开始完成一个相对复杂、在豆瓣有

    类似功能的文件托管服务。这个项目贯穿本书,在之后的章节中会

    对它继续扩展。

    第4章 这一章是Flask的进阶,包含了大量的Flask扩展的使用,还

    介绍了信号机制和Werkzeug的使用。到这里读者对Flask和Web开发已经入门,可以根据自己的想法自己做一些应用了。

    第5章 现在Web端应用对交互的要求很高,移动应用对后端的API

    需求也非常多,需要很好的异构通信方式,本章将介绍笔者对

    REST的理解,并提出一些设计API的注意事项,最后通过jQuery和

    fetch实现使用Ajax的例子,让读者了解如何让前后端通信。

    第6章 我们已经有了有实际业务逻辑的Web应用,可是用户还不

    能访问,本章将介绍如何选择应用服务器,用主流的方式在生产环

    境中运行这个应用。之前应用中只是使用了MySQL,在实际的网

    站应用中,缓存、键值对数据库、NoSQL数据库都是主流的解决方

    案,本章将一一介绍为什么要用这种技术以及怎么用。最后作为总

    结,笔者根据自己的实际经验绘制一张大型网站的架构图,并详细

    介绍其中模式选择的理由和经验。

    第7章 在第6章,Web应用已经运行起来,用户也可以访问了。但

    是如下问题也随之出现:

    应用依赖多个服务,如MySQL、Redis等,这些服务器在新环境中

    的部署是有顺序要求的,而且程序要保证一直在运行状态。

    上线过程不能自动化。每次上线都要手动执行很多命令,既耗时又

    容易出错。

    希望能及时了解和分析服务器和应用的运行状态。

    看完本章相信你就可以知道对应的解决方案了。

    第8章 Web应用运行良好,可是应用的质量还没有保证,如何在

    上线之前发现更多的Bug的需求变得越来越迫切。本章将介绍主流

    的测试方法,并用一个GitHub项目实现持续集成。

    第9章 前面介绍的是Web应用必备的内容,从本章开始介绍一些

    进阶的内容。消息队列能带来更好的用户体验,本章将介绍豆瓣用到的消息队列工具Beanstalkd,以及Celery推荐的消息队列

    RabbitMQ。如果Web产品有大量的定时任务或者其他异步任务,就

    可以使用Python界最知名的Celery解决,本书将从浅入深让读者熟

    悉Celery原理和使用方法,最后分享笔者使用的进阶实践。

    第10章 现在各个大公司都在谈服务化,通过这几年的改造和实

    践,大家都有自己的一套服务化方案,豆瓣也不例外。本章将告诉

    读者为什么要服务化、豆瓣的服务化设计,以及使用开源的Thrift

    改造文件托管服务。

    第11章 笔者在工作中经常要给各个业务方提供数据支持,如日志

    统计分析、数据报表。本章将演示如何使用纯Python代码在单个服

    务器上利用多核实现MapReduce功能,还详细讲解豆瓣工程师都在

    用的DPark,包含安装、环境配置、使用和框架化分析UVPV;接

    着将展示几个笔者在实际工作中遇到过的数据报表需求,并讲解如

    何用Pandas做数据可视化。

    第12章 这一章将详细介绍IPython和Jupyter Notebook这两个工

    具,并分享其在豆瓣对应的实践。除此之外,还列出笔者日常用来

    排错和调试的工具,包括了解Linux服务器的相关情况、性能测

    试、分析Python程序性能瓶颈三个方面。

    第13章 Web开发日常也会有一些并发编程工作,所以本章以抓取

    微信公众号文章为主线,分别使用多线程、多进程、Gevent、Future和asyncio这5种编程方式完成不同阶段的爬取任务,也深入地

    分析在它们之间如何选择。

    第14章 Python进阶并不只针对Web开发人员,对于所有Python开

    发者都有意义。本章介绍了一些非常有用的标准库模块,也有笔者

    对《Python之禅》的理解和总结的一些语法实践,还讲述了从

    Python 3移植一些有用的功能及编写Python扩展等内容。

    第15章 介绍笔者日常进行Web开发的流程和经验,还着重介绍了多个代码质量保证工具,以及豆瓣的一些质量保证实践。最后一

    节,笔者将谈谈代码评审的意义和实际经验。

    需要说明的是,章与章以及每章内的节与节之间没有明确的递进关

    系,不同产品形态让Web产品在其发展的不同阶段对技术的选择和侧重

    点都有所不同。举个例子,并不是前8章讲到的内容在产品中都用到

    了,才能在应用中引入消息队列和Celery,要根据实际情况灵活选择。

    在产品发展还没有遇到瓶颈之前就要考虑和尝试引入对应的解决方案,确保不会影响产品高速发展。使用代码示例

    书中的完整例子的代码都存放在GitHub上

    (https:github.comdongweimingweb_develop),可以在GitHub上查看

    和下载。本书提供的Vagrant和Docker环境已经包含了这个项目,但可能

    并不是最新的代码,如果遇到运行失败等问题,可以在项目的Issue页面

    先搜索是否之前有人遇到过同样问题。如果没有找到,请尝试保存本地

    修改后使用如下命令同步最新的代码,然后再运行:

    git pull--rebase origin master

    例子文件的名字在书的对应位置会提到,文件存放在对应的章节的

    子目录下,比如第3章第1节的例子,就会放在chapter3section1目录下。

    有一些文件是相对于项目根目录的,比如静态文件统一存放在static目录

    下,模板文件统一存放在templates目录下。绝大多数模板文件使用相同

    的对应章节的约定,比如第4章第2节的模板存放在

    templateschapter4section2目录下。反馈和勘误

    由于笔者水平有限,加之本书的编写稍显仓促,书中难免出现一些

    不准确甚至错误的理解和不能运行于所有环境的例子,恳请读者批评指

    正。如果你在阅读过程中有任何问题或者发现任何错误,都可以去本书

    源代码的Issue页面来提问,或者发送邮件到ciici123@gmail.com,我会

    尽量一一解答。

    本书的全部勘误将放在项目的errata.md文件中,地址是

    http:bit.ly2a9ep8V,在提交错误之前请先看看是不是之前有人已经提

    过了。

    为了促进本书读者的交流和反馈,笔者创建了一个关于Python和

    Web开发等主题的社区,地址是http:python-cn.org,关于本书的意见和

    建议也可以到这里反馈。这个平台也能帮助你认识更多阅读本书、对

    Python和Web开发有兴趣和有经验的人。致谢

    首先感谢给本书做技术评审的洪强宁(hongqn)、大妈

    (Zoom.Quiet)、清风、李迎辉(limodou)、邢犇(CNBorn)、胡阳

    (the5fire)、姚钢强(acmerfight)、蔡斌(VeryCB)和Spawnris。你

    们从技术和全书规划等角度提出了很多有用的意见和建议,让我受益匪

    浅。尤其特别感谢Zoom.Quiet,提供了大量建设性的批评意见,这些意

    见深刻而透彻,对笔者改进和重构内容帮助极大,尤其是对第3章和第5

    章。接下来,感谢写书过程中豆瓣同事对我的帮助:感谢Guillaume

    Bouriez对PIDL部分的审阅,他以法国人的严谨提出了很多有用的建

    议;感谢潘妙才让豆瓣的缓存服务运行良好,产出了非常好的使用缓存

    的文档,并对我提出的缓存问题耐心回答;感谢田忠博对我搭建DPark

    提供了很大的帮助,对DPark的特点的总结源于田老师的内部分享幻灯

    片。

    感谢电子工业出版社的编辑许艳和其他同事,本书能在8个月的时

    间出版,离不开你们的敬业精神和一丝不苟的工作态度。

    最后,最应该感谢的是我的父母、妻子和女儿,你们是我生命中最

    重要的人。尤其感谢我的妻子,在我占用大量周末、晚上的时间进行写

    作的时候,能够给予极大的宽容、支持和理解,对我和女儿悉心照顾并

    承担了全部的家务,让我能够全身心投入到写作中。

    Amy,你是我写作本书最大的动力,这是爸爸送给你2周岁的特殊

    礼物。

    2016年8月7日目 录

    推荐序一

    推荐序二

    推荐序三

    推荐序四

    业界热评

    前言

    第1章 初识Python Web开发

    Python Web开发介绍

    为什么应该选择Python作为Web开发语言

    选择Python 2还是Python 3

    Web框架介绍

    主流Web框架

    小众的Web框架

    选择Web框架时应遵循的原则

    第2章 Web开发前的准备

    搭建一个能运行的虚拟机环境

    安装VirtualBox

    使用Vagrant安装

    使用Docker安装

    包管理和虚拟环境

    包管理

    使用pip替代easy_install

    distribute、distutils和setuptools

    entry_points插件系统

    虚拟环境

    virtualenv

    virtualenv定制化

    virtualenvwrapper

    virtualenv-burrito

    autoenv

    进阶篇:pip高级用法

    命令自动补全

    普通用户安装

    编辑模式

    使用devapi作为缓存代理服务器

    PYPI的完全镜像

    第3章 Flask Web开发

    Flask入门

    安装Flask

    从Hello World开始

    配置管理

    调试模式

    动态URL规则

    自定义URL转换器

    HTTP方法

    唯一URL

    构造URL

    跳转和重定向

    响应

    静态文件管理即插视图

    蓝图

    子域名

    命令行接口

    模板

    Jinja2

    Mako

    使用MySQL

    安装MySQL和驱动

    设置应用账号和权限

    用MySQLdb写原生语句

    事务提交和回滚

    ORM简介

    使用SQLAlchemy

    使用ORM

    数据库关联

    在Flask中使用SQLAlchemy

    记录慢查询

    理解Context

    本地线程

    Werkzeug的Local

    flask.request

    使用上下文

    使用LocalProxy替代g

    从零开始实现一个文件托管服务

    首页

    重新设置图片页下载页

    预览页

    短链接页

    第4章 Flask开发进阶

    Flask的信号机制

    Blinker的使用

    Flask中内置的信号

    自定义信号

    信号订阅的高级用法

    Flask-Login中的信号

    Flask的扩展

    Flask-Script

    Flask-DebugToolbar

    Flask-Migrate

    Flask-WTF

    Flask-Security

    Flask-RESTful

    Flask-Admin

    Flask-Assets

    Werkzeug的使用

    DebuggedApplication

    数据结构

    功能函数

    密码加密

    中间件

    第5章 REST和Ajax

    什么是RESTRESTful API设计指南

    使用名词来表示资源

    关注请求头

    合理使用请求方法和状态码

    正确地使用REST

    对输出的结果不再包装

    不要做出错误的提示

    使用嵌套对象序列化

    版本

    URI失效和迁移

    信息过滤

    速度限制

    缓存

    并发控制

    使用Ajax

    第6章 网站架构

    Python应用服务器

    WSGI协议

    常见的WSGI容器

    Web服务器Nginx

    Web服务器与应用服务器的区别

    为什么要选择Nginx

    安装Nginx

    使用Nginx部署Flask应用

    缓存系统Memcached

    Libmc安装配置

    使用原生SQL缓存缓存更新策略

    Memcached使用的经验

    键值对数据库Redis

    操作Redis

    Redis应用场景

    分片和集群管理

    NoSQL数据库MongoDB

    为什么使用NoSQL

    MongoDB

    使用pymongo的例子

    使用Mongoengine的例子

    MongoDB实践经验

    大型网站架构经验

    缓存

    负载均衡

    高可用

    业务拆分

    集群

    第7章 系统管理

    进程管理Supervisor

    Supervisor组件

    配置Supervisor

    使用Supervisor

    应用部署Fabric

    Fabric应用接口

    使用Fabric管理Flask应用

    配置管理工具SaltStack和AnsibleSaltStack

    Ansible

    使用Psutil

    使用Sentry收集错误信息

    安装配置Sentry

    启动Sentry

    创建团队和项目

    配置SDK

    使用StatsD、Graphite等搭建Web监控

    配置Graphite

    使用StatsD

    配置Diamond

    发布指标项

    使用Grafana

    使用Kenshin

    第8章 测试和持续集成

    使用unittest和doctest做测试

    unittest

    doctest

    使用py.test和mock

    py.test

    mock

    持续集成

    使用Tox集成

    第9章 消息队列和Celery

    使用Beanstalkd

    使用Beanstalkc深入理解RabbitMQ

    AMQP

    虚拟主机

    插件系统

    通过Web和REST API管理RabbitMQ

    故障转移

    使用Celery

    Celery的架构

    Celery序列化

    安装配置Celery

    从一个简单的例子开始

    指定队列

    使用任务调度

    任务绑定、记录日志和重试

    在Flask应用中使用Celery

    深入理解Celery

    Celery的依赖

    任务调用

    信号系统

    Worker管理

    监控和管理Celery

    子任务

    进阶篇:Celery最佳实践

    使用自动扩展

    善用远程Debug

    合理安排任务周期

    合理使用队列和优先级保证业务逻辑的事务性

    关闭你不想要的功能

    使用阅后即焚模式

    善用Prefetch模式

    善用工作流

    第10章 服务化

    为什么需要服务化

    RPC框架

    服务化带来的问题

    微服务架构

    使用Thrift

    定义IDL文件

    服务端实现

    客户端实现

    PIDL——豆瓣的服务化实践

    PIDL架构

    第11章 数据处理

    使用MapReduce做日志分析

    使用MapReduce

    使用DPark

    分布式文件系统MooseFS

    Mesos

    配置DPark环境

    从WordCount开始

    PVUV统计

    数据报表

    发送带有样式和附件的邮件创建xlsx文件

    使用Pandas

    Pandas入门

    读取MySQL数据库

    和Flask应用集成

    第12章 帮助工具

    IPython

    IPython交互模式

    常用的Magic函数

    配置和自定义IPython

    IPython的扩展系统

    使用IPython调试复杂代码

    双进程模型

    并行计算

    Jupyter Notebook

    Notebook格式

    Notebook格式转换和预览

    为什么使用RequireJS

    在Notebook里使用Echarts

    富显示

    自定义JavaScript和CSS样式

    使用nbextension扩展Notebook

    在Notebook上使用并行计算

    调试和Debug工具

    了解Linux服务器运行情况

    性能测试

    Python程序性能分析性能调优实践

    进阶篇:定制基于IPython的交互解释环境

    进阶篇:豆瓣东西的Jupyter Notebook实践

    第13章 Python并发编程

    使用多线程

    使用Gevent

    使用多进程

    使用Future

    使用asyncio

    asyncawait

    Future

    使用aiohttp

    使用队列

    第14章 Python进阶

    使用标准库模块

    errno

    subprocess

    contextlib

    glob

    operator

    functools

    collections

    Python语法最佳实践

    命名

    使用join连接字符串

    EAFP vs LBYL

    定义类的__str____repr__方法优美的Python

    从Python 3移植

    partialmethod

    singledispatch

    suppress

    redirect_stdoutredirect_stderr

    使用CFFICython编写Python扩展

    使用CFFI

    使用Cython

    进阶篇:使用PyObjC发送通知

    第15章 Web开发项目实践

    Web项目经验总结

    开发流程

    使用合理的项目结构

    关注代码复杂度

    代码质量保证工具

    Pycodestyle对中文缩进的处理

    Flake8

    Pylint

    其他代码质量保证工具

    使用AST做静态检查

    其他静态检查工具

    编写Flake8扩展

    代码评审的意义

    作为被评审者

    作为评审者

    评审的标准第1章 初识Python Web开发

    本章将回答Python工程师关心的如下3个问题:

    为什么应该选择Python作为Web开发语言?

    在Python 2和Python 3之间如何选择?

    在这么多的Python Web框架中哪些是主流的,它们的特点是什么,该如何选择?Python Web开发介绍

    打开本书,说明你对Python Web开发是有兴趣的,或者正打算开始

    学习使用Python做Web开发,又或者已经是一个Web开发者。那么,用

    Python做Web开发的人员需要具备哪些技术能力呢?

    笔者列了一个清单:

    至少熟悉一种Python Web框架。

    熟悉Python语法。

    熟悉数据库、缓存、消息队列等技术的使用场景、使用方法等。

    日常能使用Linux或Mac系统工作。

    有性能调优经验,能快速定位问题。

    对HTMLCSSJavaScript有一定了解,有使用经验。

    Web开发需要掌握的知识很广,而且对每个知识点都要有深入的了

    解。除此之外,还要对业务有深刻理解,并能写出可维护性足够高的代

    码。

    为什么应该选择Python作为Web开发

    语言

    对于Web开发,有很多的编程语言可以选择,为什么应该选择

    Python呢?在2016年7月的TIOBE编程语言排行榜(http:bit.ly2a5jikR)中,Python已经升至第4位,可见Python现在有多么流行。现在无论PC端还

    是移动互联网的Web开发工作,对产品做的各种尝试都需要更快地拿出

    模型并进行迭代,创业公司尤甚。Python语言更好地符合了时代的需

    求,所以它也受到了越来越多的关注,越来越多的人接受Python,并在

    生产环境中使用它。个人认为Python非常适合做Web开发,理由如下:

    1. Python是一门优雅而健壮的编程语言,它继承了传统编译语言

    的强大性和通用性,同时借鉴了简单脚本和解释型语言的易用

    性。Python非常适合做快速的原型开发,很多场景下的性能问

    题可以通过使用CC++写Python扩展等方式优化解决。

    2. Python应用广泛,在大数据、算法、运维等领域都有不错的对

    应工具和库,可以有效降低产品流程中不同职位之间的技术壁

    垒,团队人员的沟通更容易,解决问题也更快。

    3. Python标准库和第三方的库很强大,有非常多的知名项目都是

    用Python编写的。

    4.从2005年Django开源,2008年Reddit开源,到2010年Flask开

    源,Python用作Web开发已经有着10多年的历史,国内的豆

    瓣、搜狐,国外的Reddit、YouTube、Instagram、Pinterest、Bitbucket、Disqus、Dropbox等公司都选择Python作为Web开发

    的语言(http:bit.ly28QKXBv)。不用担心Python可靠性与性能

    问题,因为它已经经受了时间和大规模用户并发访问的考验。

    选择Python 2还是Python 3首先需要强调的是,编程其实重在对编程思想的理解和经验的积

    累,Python 23的思想基本是共通的,只有少量的语法有差别甚至不兼

    容。当对Python熟悉到一定程度时,即使只会Python 2也可以在很短的

    时间就能掌握Python 3代码的编写。

    Python社区曾经于2014年初在python-dev(http:bit.ly28RlJDV)、hacker news(http:bit.ly28RU1YZ)等渠道针对这个问题做过一个调

    查,部分结果如下(http:bit.ly28QKyUe):

    1.97.51%的用户还在写Python 2的代码。

    2.60%的用户在写Python 3的代码。

    3.78.09%的用户更多地写Python 2的代码。

    4.77.09%的用户认可Python 3。

    有以下主流操作系统已默认使用Python 3:

    Arch Linux(http:bit.ly28RlNDN)

    Ubuntu 16.04 LTS(http:bit.ly28QL8Nm)

    Fedora(http:bit.ly28RUdHy)

    Django 2.0将不再支持Python 2。

    现在Python 2只是在做一些Bug修复、新硬件和操作系统兼容性相

    关的维护工作,不再有新的功能加入。但是话说回来,虽然Python 2只

    会支持到2020年(http:bit.ly28QL4ND),但是对于生产环境,尤其是

    重要的应用,不会为了这么一个理由就迁移到Python 3,只能是在新写

    的项目中抛开这个历史包袱。所以在未来相当长的时间内,Python 2都会存在。但如果你愿意拥抱变化,义无反顾地选择Python 3吧。如果是

    为了满足现在工作中的需要,尤其是依赖的软件只能运行在Python 2

    下,那还是首选Python 2。Web框架介绍

    Python的Web框架可算是百花齐放,各种框架和微框架数不胜数,关于Python框架孰优孰劣的讨论一直没有间断过。这种争论给Web开发

    工程师们带来了很大的困扰,尤其对初中级的工程师来说,不知道如何

    选择。本节我们就来介绍当前知名的Web框架的特点及其应用场景。

    主流Web框架

    主流的Web框架有以下几种。

    Django

    Django最初是被开发用来管理劳伦斯出版集团旗下一些以新闻内容

    为主的网站的,它以比利时的吉普赛爵士吉他手Django Reinhardt来命

    名,它和Flask是使用最广泛的Python Web框架。Django能如此知名很大

    程度上是因为提供了非常齐备的官方文档,它提供了一站式的解决方

    案,包含缓存、ORM、管理后台、验证、表单处理等,使得开发复杂

    的数据库驱动的网站变得很简单。但正因为它坚持自己对于Web框架的

    理解,系统耦合度太高,替换掉内置的功能往往需要花费一些功夫,所

    以学习曲线也相当陡峭。

    Flask

    Flask是一个轻量级Web应用框架,它基于Werkzeug实现的WSGI和

    Jinja2模板引擎。Flask的作者是Armin Ronacher,本来这只是作者愚人节开的一个玩笑,但是开源之后却受到Python程序员的喜爱,目前在

    GitHub上的Star数量已经超过了Django。它的设计哲学和Django不同:

    只保留核心,通过扩展机制来增加其他功能。Flask用到的依赖都是

    Pocoo团队开发的。Pocoo团队其他的项目还有Pygments、Sphinx、以及

    lodgeit。Flask的扩展环境非常繁荣,基本上Web应用的每个环节都有对

    应的扩展供选择,就算没有对应的扩展也能很方便地自己实现一个。

    Pyramid

    Pyramid在国内知名度并不高,主要原因是中文文档匮乏,其高级

    用法需要通过阅读源代码获取灵感。尽管被Django和Flask的光芒遮蔽,但是它的性能要比Flask高。Pyramid的灵感来源于Zope、Pylons 1.0和

    Django。在我们的传统观点里,小框架通常牺牲了大框架的特色,反之

    亦然。但是事实上我们不能真正把控一个应用程序最终的发展情况,而

    Pyramid在努力朝着胜任不同级别应用的框架的方向在走。虽然它默认

    使用Chameleon和Mako模板,但很容易切换成Jinja2,甚至可以让多种

    模板引擎共存,通过文件后缀名来识别。豆瓣赞赏和豆瓣钱包等产品就

    是基于此框架实现的,代码量级和Flask相同,性能比Flask要略高。

    Bottle

    Bottle也是一个轻量级的Web框架。它的特点是单文件,代码只使

    用了Python标准库,而不需要额外依赖其他第三方库。它更符合微框架

    的定义,截止到今天只有4100多行的代码。

    Tornado

    Tornado全称Tornado Web Server,最初是由FriendFeed开发的非阻

    塞式Web服务器,现在我们看到的是被Fackbook收购后开源出来的版本。它和其他主流框架有个明显的区别:它是非阻塞式服务器,而且速

    度相当快。得益于其非阻塞的方式和对epoll的运用,Tornado每秒可以

    处理数以千计的连接,这意味着对于长轮询、WebSocket等实时Web服

    务来说, Tornado是一个理想的Web框架。

    Web.py

    Web.py也是一个微框架,由Reddit联合创始人、RSS规格合作创造

    者、著名计算机黑客Aaron Swartz开发。Web.py使用基于类的视图,简

    单易学却功能强大。

    小众的Web框架

    Quixote

    Quixote是由美国全国研究创新联合会(Corporation for National

    Research Initiatives,CNRI)的工程师A.M.Kuchling、Neil Schemenauer

    和Greg Ward开发的一个轻量级Web框架,它简单、高效、代码简洁。

    豆瓣的大部分用户产品都使用定制版的Quixote作为Web框架,虽说这有

    历史原因(当时DjangoFlask等框架还没有出现),但是也证明这个始

    于2000年的框架是可以经受时间考验的。它使用目录式的URL分发规

    则,用Python来写模板。PTL模板更适合程序员,但是并不适合美工参

    与前端代码的编写和修改,豆瓣在开发中使用了Mako替代PTL。不建议

    在生产环境选用Quixote。

    Klein

    Klein是Twisted组织开源出来的基于werkzeug和twisted.web的微框架。Flask很不错,但是不能使用异步非阻塞的方式编程,根本原因是它

    和Django、Pyramid一样,都基于WSGI,而WSGI的接口是同步阻塞

    的。Klein用法非常像Flask,却可以使用异步的方式开发Web应用。

    选择Web框架时应遵循的原则

    介绍了这么多的框架,那么在工作中怎么选择呢?以下是笔者总结

    的一些原则:

    1.选择更主流的框架。因为它们的文档更齐全,技术积累要更

    多,社区更繁盛,能得到更好的支持。

    2.关注框架的活跃情况。关注项目的更新频率、Issue和Pull

    Request(在本书中都简称PR)的响应情况。如果一个项目已经

    很长时间没有更新了,或者有一堆的问题需要解决但是没有得

    到回应,就不应该将这样的框架放在生产环境中。

    3.确认选择的框架是否足够满足需求。没有最好的框架,只有最

    合适的框架。你所选择的Web框架不仅需要满足当前的需求,也要充分考虑项目发展一段时间之后的情况,即前瞻性。如果

    在做选择时有个人喜好这样的因素,需要确认自己有能力对选

    择的Web框架提供支持,避免盲目选择而导致将来推倒重来的

    情况。

    4.注意媒体时效性。在做选择的时候可能会参考网络上的一些文

    章,但是需要注意其发表时间。举个例子,看了一篇2012年的

    博客,里面说应该选择A而不是B,并给了多个理由。而现在的情形可能已经发生了变化:B经过很久的努力已经做得更优秀或

    者2012年之后出现了更优秀的C。

    5.客观看待媒体的观点。媒体的观点并不一定是正确的(或者不

    是全部正确),如果不是官方的说明,就应该保持怀疑和谨慎

    的态度,取其精华去其糟粕,切勿完全拿来主义,应该真正做

    实践验证之后再做决定。

    对于上述的Web框架,可以参考如下几条建议:

    1.如果是为了供演示或者不太考虑长期发展,只是需要用到数据

    库的CURD操作、写REST API之类的简单型应用,框架的选择

    非常宽松,用着顺手的即可。

    2.如果你初学Web框架,建议选择Flask作为入门框架,学习曲线

    相对Django而言要低很多。等熟悉Flask之后再学习Django,就

    会容易很多。

    3.Pyramid和Django都是面向大型应用的,Pyramid更灵活,开发

    者空间大得多,值得考虑。

    4.网站性能出现问题时,往往问题不只出在Web框架和编程语言

    上,做选择时在环境中按实际产品逻辑测试一段时间即可得到

    结论,但是不要只相信看到的一些性能对比文章,尤其不要做

    无意义的Hello World级别的测试。第2章 Web开发前的准备

    本章介绍Web开发的一些准备工作,主要包含如下内容:

    环境的准备,以便读者能够使用Vagrant或者Docker提供的Ubuntu环

    境运行书中的例子。

    介绍包管理工具pip及一些高级用法。

    实现PYPI的缓存代理和完全镜像。

    使用virtualenv及其扩展实现虚拟环境管理。搭建一个能运行的虚拟机环境

    Ubuntu是Linux发行版里面被用作个人桌面最多的系统,现在已经

    有很多公司选择使用Ubuntu Server作为生产环境的操作系统。

    笔者选择了发布于2016年4月21日,版本代码名为“Xenial Xerus”的

    Ubuntu 16.04 LTS。LTS是Long Term Support (长期支持)的缩写,这

    样的版本一般桌面版官方支持3年,服务器版支持5年。

    下面使用Docker和Vagrant安装Ubuntu的章节是根据写作本书时的

    官方文档编写的,当你阅读到这里的时候有可能某些内容已经变更,请以官方文档为准。

    安装VirtualBox

    VirtualBox是Oracle开源的虚拟化系统,它支持Linux、OS X、Windows等平台,Docker和Vagrant环境都需要使用它作为宿主机。到官

    方网站下载对应平台的最新版并安装。安装过程很傻瓜化,按提示一步

    一步执行到安装完成即可。

    使用Vagrant安装为什么选择Vagrant?原因如下:

    1.Vagrant是一个操作虚拟机的工具,它会很快地完成一套开发环

    境的部署,也解决了各个开发环境不一致的问题,减少了重复

    配置环境而造成的时间和精力上的浪费。举个例子,在没有用

    Vagrant之前,新员工加入后常常需要一到两天的时间搭建完整

    的开发环境,而有了Vagrant,直接启动就好了。先不需要了解

    所有相关环境的知识和细节,在工作中再慢慢熟悉就行了。

    2.它底层支持VirtualBox、VMware甚至AWS作为虚拟机系统,可

    以满足不同用户的需要。

    3.可以通过“vagrant provision”,使用Shell脚本或者主流的配置管

    理工具(如Puppet、Ansible等)对软件进行自动安装、更新和

    配置管理。

    安装Vagrant

    目前Rubygems上Vagrant只更新到1.5.0,不支持VirtualBox 5.0及以

    上版本。需要到官方网站选择对应的平台下载并安装。

    安装完成后检查一下是否安装成功:

    > vagrant--version

    Vagrant 1.8.3

    需要使用Vagrant 1.8.3及以上版本支持Ubuntu 16.04 LTS。使用Vagrant

    一个打包好的操作系统在Vagrant中称为Box,实际上它是一个zip

    包,包含了Vagrant的配置信息和VirtualBox的虚拟机镜像文件。

    默认的Ubuntu系统需要进行配置,如改成使用aliyun的源,安装

    Python等软件,添加用户ubuntu等。为了方便读者,可以使用笔者打包

    好的Box(dongweimingweb_develop)。首先,克隆本书源代码并进入

    项目目录:

    > git clone https:github.comdongweimingweb_develop

    > cd web_develop

    项目中包含了Vagrantfile文件,不需要初始化:

    Vagrant.configure(2) do|config|

    config.vm.box=dongweimingweb_develop

    config.vm.hostname=WEB

    config.vm.network:forwarded_port, guest:9000, host:9000

    config.vm.network:forwarded_port, guest:3141, host:3141

    config.vm.network:forwarded_port, guest:5000, host:5000

    config.ssh.username=ubuntu

    config.ssh.password=ubuntu

    config.ssh.insert_key=false

    config.ssh.private_key_path=[~.sshid_rsa]

    config.vm.provisionfile, source:~.sshid_rsa.pub, destination:~.ssh

    authorized_keys

    config.vm.providervirtualboxdo|v| v.customize [modifyvm,:id,--name,web_dev,--memory,1536]

    end

    config.vm.networkpublic_network, bridge:en0:Wi-Fi (AirPort)

    end

    Vagrantfile保存了虚拟机的各项配置。上述设置的用途如下:

    1.使用笔者定制的Box,名字为dongweimingweb_develop。

    2.设置虚拟机的主机名。

    3.forwarded_port用来设置端口转发,“guest:9000, host:9000”表示

    访问本机9000端口的流量就会转发到虚拟机上的9000端口,反

    之亦然。

    4.没有使用vagrant作为默认用户,这是因为代码中有些指定了目

    录地址,为了兼容Docker容器,使用中立的用户ubuntu。

    5.customize语句把虚拟机在VirtualBox中的显示名改为web_dev,内存为1536 MB。

    6.config.vm.network用来添加一个桥接网卡,它将使用DHCP获得

    IP。

    定制的Box基于官方的ubuntuxenial64,笔者在实际使用中发现不能

    直接登录,这里我们将创建一个SSH密钥用于自动登录。如果之前没有

    创建过SSH密钥,需要执行如下命令并回车:

    > ssh-keygen执行完毕会生成~.sshid_rsa(密钥)和~.sshid_rsa.pub(公钥)。

    现在启动虚拟机:

    > vagrant up

    启动的时候会检查本地是否有这个Box,没有的话就会下载。所以

    第一次花的时间会比较长。

    第一次启动完成后需要使用配置脚本来初始化系统环境:

    > vagrant provision

    ==>default:Running provisioner:file...

    provision会执行Vagrantfile中定义的file命令,把本机的

    ~.sshid_rsa.pub拷贝到目标服务器并保存为~.sshauthorized_keys。

    启动完成就可以登录虚拟机了:

    > vagrant ssh

    登录之后就可以直接验证本书提到的内容了。

    使用Docker安装

    Docker是dotCloud开源的一个使用Go语言编写的基于Linux容器

    (Linux Containers,LXC)的容器引擎。它有以下优点:

    性能卓越。在以前的虚拟化方案中,虚拟机都是一个完整的操作系

    统,这本身就会占用CPU、内存、硬盘等资源。而Docker是“操作系统级别的虚拟化”,可以达到秒级启动。IBM曾发表过一篇关于

    虚拟机和Linux Container性能对比的论文(http:domino.

    research.ibm.comlibrarycyberdig.nsfpapers0929052195DD819C85257D2300681E7BFilerc25482.pdf

    论文中实际测试了虚拟机和LXC在CPU、内存、网络的负载等方面

    的情况,结果显示Docker容器本身几乎不占用什么开销。

    可移植性带来了工作效率的提升。没有Docker之前,运维和开发工

    作中经常会发生这样的情况:本地开发环境正常,但是上线就出问

    题。这就需要花时间找原因然后解决。而Docker是“一次封装,到

    处运行”,开发者只需要关注开发,运维人员只需要关注部署,比

    如要做服务器迁移,重新部署和调试这样的工作就可以省去了——

    在新的服务器上启动需要的容器就可以了。

    安装Docker

    Docker官网的文档非常详细。打开菜单第一栏“Install”,根据你当

    前使用的桌面系统选择对应的版本:如果使用Max OS X,就选

    择“Installation on Mac OS X”;如果使用Ubuntu,就先打开“Linux”子菜

    单,再选择“Installation on Ubuntu”。也可以选择直接安装Docker的二进

    制文件“Installation from binaries”。

    这时候可能会显示对你选择的系统的要求。比如选择“Ubuntu”,会

    提示支持如下版本的Ubuntu:

    Ubuntu Xenial 16.04 (LTS)

    Ubuntu Wily 15.10

    Ubuntu xenial 14.04 (LTS)

    Ubuntu Precise 12.04 (LTS)如果你使用的系统不在上述列表中,不代表不能正常使用Docker,只是如果出现问题可能不会得到官方的修复支持,所以笔者建议升级到

    它要求的版本范围。

    对于Max OS X,按照官方文档进行,然后点击“Docker Quickstart

    Terminal”选项就可以进入Docker Shell。它会进行一系列的初始化,最

    后会提示:

    docker is configured to use the default machine with IP 192.168.99.100

    For help getting started, check out the docs at https:docs.docker.com

    上面提到的192.168.99.100是Docker创建的虚拟机的IP,之后访问应

    用其实都是在请求这个IP。如果忘记了也可以通过如下命令获得:

    > docker-machine ip

    192.168.99.100

    如果看到类似如下的输出就说明Docker安装成功了:

    docker--version

    Docker version 1.11.2, build b9f10c9

    下载镜像

    在Docker中镜像称为Image。为了节省读者的时间,笔者已经上传

    了基于Ubuntu:16.04 LTS的镜像。

    1.dongweimingweb_develop:dev:dev标签包含了基本环境,比如

    使用aliyun的源,安装Python、Git、IPython、net-tools等软件,克隆本书源码,添加用户ubuntu并默认使用ubuntu这个用户等。所做的全部前期工作都可以参考项目下的Dockerfile文件。这个

    版本适合从零开始跟着笔者一起完成每一节例子的读者。

    2.dongweimingweb_develop:也就是默认的latest标签,它可以直

    接运行绝大多数应用和例子。这个版本会经常更新,适合有一

    定基础,想直接看到运行效果或者会跳跃着看书的读者。

    执行如下命令会自动下载镜像:

    > docker pull dongweimingweb_develop:dev

    Docker会查看镜像是否已经加载到Docker主机上,如果没有,它就

    会从镜像仓库Docker Hub下载这个镜像。下载完成后可以看到类似如下

    的镜像列表:

    > docker images

    REPOSITORY TAG IMAGE ID CREATED SIZE

    dongweimingweb_develop dev 43fb02d9c1a3 2 weeks ago 292.2 MB

    dongweimingweb_develop latest 5895e8d64924 34 seconds ago 6.075 GB

    要进入容器应该使用如下命令:

    > docker run--name web_dev-it-p 9000:9000-p 3141:3141-p 5000:5000 dongweiming

    web_developbinzsh

    这个命令有如下含义:--name指定了容器的名字为web_dev,如果不指定,将由系统随机

    取一个名字。

    -p可以显式地暴露特定端口,比如9000:9000,就表示web_dev这个容器里面的9000端口可以通过192.168.99.100:9000访问。

    binzsh是登录容器的默认Shell。

    进入容器后,默认使用ubuntu这个用户,并切换

    到homeubuntuweb_develop目录下。

    从容器退出之后,容器就关闭了,重新登录容器的方法如下:

    > docker start web_dev 启动容器

    web_dev

    > docker attach web_dev 要回车2次

    Docker虚拟机端口转发

    之前提到,在Docker环境中访问应用需要使用

    http:192.168.99.100:PORT这个地址,由于Vagrant做了端口转发,直接

    在宿主机上访问http:127.0.0.1:PORT即可。那么,可不可以统一呢?当

    然可以。

    首先获得Docker虚拟机的名字:

    > docker-machine inspect|grep MachineName

    MachineName:default,Docker虚拟机的名字是default。通过如下的Shell命令即可添加本机

    与容器的端口镜像:

    > for port in 3141 5000 9000

    do

    VBoxManage controlvmdefaultnatpf1tcp-portport,tcp,127.0.0.1,port,,port;echo port

    done

    VBoxManage是Virtualbox提供的命令行管理工具,通过它能完成很

    多用GUI不能实现的工作。现在已经可以用http:127.0.0.1:PORT这样的

    地址访问了。包管理和虚拟环境

    包管理

    利用Python工作不可避免需要使用第三方包,目前安装第三方包的

    方法有以下三种:

    通过Python社区开发的pip、easy_install等工具。

    使用系统本身自带的包管理器(yum、emerge、apt-get等)。

    通过源码安装(python setup.py install)。

    其中的第一种方法,最推荐使用pip。

    第三方包主要分布在The Python Package

    Index(https:pypi.python.orgpypi)官方的仓库(简称PYPI)、GitHub、Bitbucket等代码托管服务上。

    使用pip替代easy_install

    pip是一个用来安装和管理Python包的工具,它是easy_install的替代

    品,也是目前社区的主流工具。之所以能成为主流,有以下的原因:

    pip已经内置到Python 2.7.9和Python 3.4及其以上的版本里面。

    easy_install只支持安装,没有提供卸载、展示当前已安装的包列表

    等功能。pip支持二进制包使用wheel格式(后缀是.whl),而easy_install不支

    持。

    pip能非常好地支持虚拟环境工具virtualenv。

    支持多种版本工具格式的包的下载和安装。

    可以集中管理项目依赖列表(文件名字一般叫作

    requirements.txt),使用-r选项安装这些依赖。

    我们使用的虚拟机默认没有安装pip,可以使用如下方式安装它:

    > sudo apt-get install python-pip-yq

    系统自带的pip版本比较低,可使用pip的自更新来升级:

    > sudo pip install pip-U-q -q表示静默安装,减少过程输出

    > pip--version

    pip 8.1.2 fromusrlocallibpython2.7dist-packages (python 2.7)

    笔者建议平时也经常这样将pip更新到最新版。

    distribute、distutils和setuptools

    在Python发展史上出现了很多创建和发布包的工具。当你想要把自

    己的项目分享出去,放到PYPI或者其他托管服务上的时候,就需要借助

    这样的工具来构建和分发项目。我们先了解一下常见的3个工具。

    distribute:setuptools的一个分支,在setuptools 0.7时被合并回setuptools,现在已经被弃用。

    distutils:早在1998年它就被内置到Python标准库里,但是只提供了

    有限的支持。

    setuptools:它是用来解决distutils的限制的替代品,但是需要额外

    的安装。和distutils相比,它有很多优点。

    可以创建Eggs和Wheel(https:wheel.readthedocs.orgenlatest)格

    式的包。

    自带easy_install,能帮助你找到、下载、安装以及更新需要使用的

    包。

    支持PYPI上传,可以很方便地把本地项目发布到PYPI。

    支持测试集成。

    提供了更多的功能函数和额外特性。

    除非项目的环境依赖简单到只需要用到distutils就可以了,否则推荐

    使用setuptools包。如果是一个开源项目,建议使用类似下面这样的兼容

    代码:

    try:

    from setuptools import setup

    except ImportError:

    from distutils.core import setup

    还有几个值得一提的工具:

    distlib(https:bitbucket.orgpypadistlib),它正致力于取代

    distutils,未来有希望进入标准库。应该对distlib保持关注。

    pbr(https:launchpad.netpbr),全称“Python BuildReasonableness”,是setuptools的辅助工具,最初是为OpenStack开

    发的,它借鉴了distutils 2,利用setup.cfg文件承载元数据,下面提

    到的virtualenvwrapper(http:bit.ly28UjAMb)就使用了pbr打包。

    entry_points

    发布的包经常需要一个(或多个)可执行的入口,以便用户直接执

    行和调用。比如flake8,安装之后在终端就可以执行flake8这个命令:

    > cat`which flake8`

    !usrbinpython

    --coding:utf-8--

    import re

    import sys

    from flake8.main import main

    if__name__=='__main__':

    sys.argv[0]=re.sub(r'(-script\.pyw|\.exe)?', '', sys.argv[0])

    sys.exit(main)

    回忆一下,我们安装的时候并没有特别地复制一个文件

    到usrlocalbinflake8,这是怎么实现的呢?要感谢setuptools提供了解决

    方案。先看一下setup.py(http:bit.ly28RVQVG)中的entry_points部

    分: entry_points={

    'distutils.commands':['flake8=flake8.main:Flake8Command'],'console_scripts':['flake8=flake8.main:main'],'flake8.extension':[

    'F=flake8._pyflakes:FlakesChecker',],}

    其中有个console_scripts的键,表示注册一个叫作flake8的系统命

    令,这个命令会调用flake8.main的main函数,安装的时候由setuptools来

    帮助我们生成了usrlocalbinflake8这个文件。选择这种方式,而不是直

    接复制文件,是基于如下原因:

    没办法预先知道Python解释器的版本和位置。

    很难确定会安装在哪里。

    无法优雅地解决可移植到不同系统上的问题。

    当然你可能看到过这样的写法:

    !usrbinpython

    EASY-INSTALL-ENTRY-SCRIPT:'flake8==2.5.1','console_scripts','flake8'

    __requires__='flake8==2.5.1'

    import sys

    from pkg_resources import load_entry_point

    if__name__=='__main__':

    sys.exit( load_entry_point('flake8==2.5.1', 'console_scripts', 'flake8'))

    它和上面命令的作用是一样的,只不过它是使用easy_install或

    者“python setup.py install”安装生成的,load_entry_point可以去抽取入口

    点是“flake8”的键并定位到flake8.main,然后运行main函数。

    使用entry_points的优点,就是可以让这些入口点能够被其他Python

    程序动态发现包所提供的功能,但是对应的代码的耦合度非常低。

    插件系统

    entry_points机制还能直接作为插件系统,比如flake8项目,上面提

    到它的entry_points中有这样一段:

    'flake8.extension':[

    'F=flake8._pyflakes:FlakesChecker',]

    flake8的文档中介绍了如何写一个插件

    (http:flake8.readthedocs.orgenlatestextensions.html),就是使用了

    entry_points作为插件机制。我们看一下已有的flake8插件之一pep8-

    naming (https:github.comPyCQApep8-naming)的entry_points的一部

    分(https:github.comPyCQApep8-namingblobmastersetup.pyL47):

    'flake8.extension':[

    'N8=pep8ext_naming:NamingChecker',]我们先安装pep-naming:

    > pip install--user pep8-naming

    > flake8--version

    2.5.1 (pep8:1.5.7, pyflakes:0.8.1, naming:0.3.3, mccabe:0.3.1) CPython 2.7.6 on

    Linux 可以看到已经出现了naming:0.3.3

    它们使用了相同的键flake8.extension,是怎么工作的呢?看一下

    flake8的插件实现(篇幅所限,此处只截取了相关的代码):

    import pep8

    from pkg_resources import iter_entry_points

    extensions=[]

    def_load_entry_point(entry_point, verify_requirements):

    if hasattr(entry_point, 'resolve') and hasattr(entry_point, 'require'):

    if verify_requirements:

    entry_point.require

    plugin=entry_point.resolve

    else:

    plugin=entry_point.load(require=verify_requirements)

    return plugin

    for entry in iter_entry_points('flake8.extension'):

    checker=_load_entry_point(entry, verify_requirements=False) pep8.register_check(checker, codes=[entry.name])

    extensions.add((checker.name, checker.version))

    这样就实现了一个简单的插件系统。

    要查看当前环境全部的入口点,可以使用

    iter_entry_points(None)。

    虚拟环境

    Python工程师工作中常常遇到这样的场景:系统自带的Python是

    2.6,却需要用到Python 2.7中的某些特性;不同的项目之间使用不同版

    本的某些包,但是因为某些原因(比如有依赖冲突)却不能都升级到最

    新版本。

    所有的包都共用一个目录,很容易出现不小心更新了项目A的依

    赖,却影响了项目B用到的依赖的情况。这个时候就需要对环境进行隔

    离,使用虚拟环境让全局的site-packages目录非常干净和可管理。

    Python社区中创建和管理虚拟环境的工具有virtualenv和pyvenv。这

    些工具可以帮助你快速创建一个单独、干净的Python环境,你可以把所

    需的包安装到各自孤立的环境中。

    virtualenv先安装virtualenv:

    sudo pip install virtualenv

    现在创建一个Python环境:

    > virtualenv venv

    New python executable inhomeubuntuvenvbinpython

    Installing setuptools, pip, wheel...done.

    virtualenv默认会创建一个包含了Python可执行文件、常用的标准

    库、激活virtualenv环境的脚本的目录。

    使用source激活virtualenv环境:

    > source venvbinactivate

    (venv)> which python 注意终端提示的改变,前面添加了“(venv)”前缀。

    homeubuntuvenvbinpython

    可以看到已经不再使用系统环境变量中的Python了。如果要退出虚

    拟环境,可以取消激活:

    (venv)> deactivate

    virtualenv定制化

    virtualenv默认只是生成一个非常标准的Python环境,而在实际使用

    中,项目都会有第三方包的依赖,会出现多个项目依赖相同的包的情况。举个例子,当项目有严格的代码规范要求和集成测试要求时,那么

    每个项目可能都需要py.test、flake8之类的基础包,通常还会添加

    IPython这样的增强交互工具。除此之外,项目还可能有初始化的需要,要求在创建虚拟环境的时候做一些额外的工作。那么,对于这种情况,就可以生成一个定制的virtualenv脚本。

    本节我们展示一个在生成venv环境的同时安装flake8的自定义脚

    本。首先让ubuntu这个用户对virtualenv文件可写,方便我们直接替换:

    > sudo chown ubuntu:ubuntu`which virtualenv`

    生成自定义脚本的内容如下(create-venv-script.py):

    import subprocess

    import virtualenv

    virtualenv_path=subprocess.check_output(['which', 'virtualenv']).strip

    EXTRA_TEXT='''

    def after_install(options, home_dir):

    subprocess.call(['{}binpip'.format(home_dir), 'install', 'flake8'])

    '''

    def main:

    text=virtualenv.create_bootstrap_script(EXTRA_TEXT, python_version='2.7')

    print 'Updating%s'%virtualenv_path with open(virtualenv_path, 'w') as f:

    f.write(text)

    if__name__=='__main__':

    main

    生成定制的virtualenv脚本:

    > python chapter2section2create-venv-script.py

    Updatingusrlocalbinvirtualenv

    usrlocalbinvirtualenv已经被替换成定制的版本了。如果不想全局

    替换,可以把virtualenv_path换成其他路径。

    现在生成一个虚拟环境,就会自动安装flake8了:

    > virtualenv tmp

    New python executable inhomeubuntuweb_developtmpbinpython2.7

    Also creating executable inhomeubuntuweb_developtmpbinpython

    Installing setuptools, pip, wheel...done....

    Installing collected packages:mccabe, pyflakes, pep8, flake8

    Successfully installed flake8-2.5.4 mccabe-0.4.0 pep8-1.7.0 pyflakes-1.0.0

    > ll tmpbinflake8

    -rwxrwxr-x 1 ubuntu ubuntu 239 May 26 09:51 tmpbinflake8

    Virtualenv定制脚本接受3个扩展函数。extend_parser(optparse_parser):添加额外的选项。

    adjust_options(options, args):改变当前的选项。

    after_install:在默认的环境安装好之后,执行其他工作,主要通过

    这个函数完成定制。现在做一个更复杂的定制,实现如下需求:

    增加一个选项,可以额外安装指定的包。

    假如没有设置这个选项,在终端发出警告提示(warn)。

    默认的虚拟环境都安装在指定的目录之下(~venv)。

    看一下升级之后EXTRA_TEXT的内容(create-venv-

    script_v2.py):

    EXTRA_TEXT='''

    ROOT_PATH='homeubuntuvenv'

    def extend_parser(parser):

    parser.add_option(

    '-r','--req', action='append', type='string', dest='reqs',help=specify additional required packages, default=[])

    def adjust_options(options, args):

    if not args:

    return

    base_dir=args[0]

    args[0]=join(ROOT_PATH, base_dir)

    def after_install(options, home_dir):

    if not options.reqs:

    logger.warn('Warn:You maybe need specify some required packages!')

    for req in options.reqs:

    subprocess.call(['{}binpip'.format(home_dir), 'install', req])

    '''

    adjust_options中能直接用join是因为virtualenv初始化的时候已经

    设置了join=os.path.join。

    重新生成virtualenv:

    > python~web_developchapter2create-venv-script2.py

    Updatingusrlocalbinvirtualenv

    看一下升级后的效果:

    > virtualenv-h|grep req 增加的新选项

    -r REQS,--req=REQS specify additional required packages

    > virtualenv tmp

    New python executable inhomeubuntuvenvtmpbinpython2.7

    Also creating executable inhomeubuntuvenvtmpbinpython Installing setuptools, pip, wheel...done.

    Warn:You maybe need specify some required packages!

    把tmp安装到了homeubuntuvenv目录下,最后发出警告提示。

    现在创建一个自动安装Flake8和Jinja2的虚拟环境:

    > virtualenv tmp2-r flake8-r jinja2

    virtualenvwrapper

    virtualenvwrapper是对virtualenv的功能扩展,它有如下用途:

    用来管理全部的虚拟环境。

    能方便地创建、删除和拷贝虚拟环境。

    用单个命令就可以切换不同的虚拟环境。

    可以使用Tab补全虚拟环境。

    支持用户粒度的钩子支持。

    使用如下方式安装:

    > sudo pip install virtualenvwrapper

    先初始化virtualenvwrapper:

    > export WORKON_HOME=~venv

    > sourceusrlocalbinvirtualenvwrapper.sh

    初始化之后~venv目录也会添加一些用户级别的virtualenvwrapper的钩子模板。通常上述两行会放在shell的配置里面,比如~.zshrc,这样每

    次登录时就自动初始化了。

    初始化时给系统添加了一些虚拟环境相关的函数,比如用来创建虚

    拟环境的mkvirtualenv:

    > mkvirtualenv venv1

    (venv1)> deactivate 退出虚拟环境的命令还是一样的

    mkvirtualenv还会添加如下5个项目级别的钩子模板。

    predeactivate:在虚拟环境取消激活之前执行。

    postdeactivate:在虚拟环境取消激活之后执行。

    preactivate:在虚拟环境激活之前执行。

    postactivate:在虚拟环境激活之后执行。

    get_env_details:使用lsvirtualenvshowvirtualenv等命令时,对于当

    前环境的额外钩子,可以添加虚拟环境介绍等内容。

    再创建一个新的虚拟环境,并添加一个钩子,在取消激活之后输

    出“Deactivated!”:

    > mkvirtualenv venv2

    (venv2)> cathomeubuntuvenvvenv2binpostdeactivate

    !usrbinzsh

    This hook is sourced after this virtualenv is deactivated.

    echo 'Deactivated!'

    (venv2)> deactivate

    Deactivated!现在已经有两个虚拟环境了,可以使用Tab管理它们:

    > workon输入workon, 然后按Tab

    venv1 venv2

    > workon venv1 选择venv1

    (venv1)> workon venv2 直接使用workon就可以切换到venv2

    (venv2)>

    其他常用的命令

    lsvirtualenv:列出全部的虚拟环境。

    showvirtualenv:列出单个虚拟环境的信息。

    rmvirtualenv:删除一个虚拟环境。

    cpvirtualenv:拷贝虚拟环境。

    allvirtualenv:对当前虚拟环境执行统一的命令。比如,要给venv1

    和venv2都安装flake8,就可以用allvirtualenv pip install flake8。

    cdvirtualenv:可以直接切换到虚拟环境的子目录里面。

    (venv1)> cdvirtualenv bin

    (venv1)> pwd

    homeubuntuvenvvenv1bin

    cdsitepackages:和cdvirtualenv同理,切换到虚拟环境的site-

    packages目录下。

    lssitepackages:列出site-packages目录下的目录。

    用户级别的钩子脚本用户级别的钩子脚本都在VIRTUALENVWRAPPER_HOOK_DIR这

    个变量的目录下,默认是WORKON_HOME变量指定的目录。

    常用的钩子脚本如下。

    get_env_details:当不带参数执行workon时执行。

    postmkvirtualenv:在虚拟环境创建和激活之后执行。

    preactivate:在虚拟环境激活之前执行。

    postactivate:在虚拟环境激活之后执行。

    predeactivate:在虚拟环境取消激活之前执行。

    postdeactivate:在虚拟环境取消激活之后执行。

    prermvirtualenv:在虚拟环境删除之前执行。

    postrmvirtualenv:在虚拟环境删除之后执行。

    更多的钩子脚本类型可以在这里找到:Per-User

    Customization(http:bit.ly28ReOu6)。

    virtualenv-burrito

    virtualenv-burrito(http:bit.ly28UjSml)是一个安装、配置

    virtualenv和virtualenvwrapper及其依赖的傻瓜式工具。无须上面烦琐的

    过程,只需要一步即可:

    > curl -sL https:raw.githubusercontent.combrainsikvirtualenv-burritomaster

    virtualenv-burrito.sh|SHELL

    它自动把初始化脚本放在homeubuntu.zprofile里面,这样在下次登

    录之后就可以使用了。如果想立刻使用初始化脚本,可以用如下语句: sourcehomeubuntu.venvburritostartup.sh

    startup.sh会自动创建了~.virtualenvs作为WORKON_HOME,用法

    和virtualenvwrapper是一样的。

    使用如下命令即可对virtualenv和virtualenvwrapper进行更新:

    > virtualenv-burrito upgrade

    本书所有Python 2的例子都使用了这个虚拟环境。

    autoenv

    autoenv(http:bit.ly290YIQG)让你在切换目录的时候可以完成自

    动激活虚拟环境(如果有的话)等定制操作。

    先安装它:

    > sudo pip install autoenv

    > sourceusrlocalbinactivate.sh

    我们需要做的就是在目录下新建一个.env文件,然后修改这个文

    件,添加激活命令就好了:

    > touch .env

    > echosource sourcehomeubuntu.virtualenvsvenvbinactivate> .env这里一定要用完整的绝对路径,否则执行cd web_developsubdir

    之类的命令会让虚拟环境激活失败。

    效果如下:

    > cd 先切换到其他目录

    > cd web_develop 出现如下提示

    autoenv:

    autoenv: WARNING:

    autoenv: This is the first time you are about to sourcehomeubuntuweb_develop.env:

    autoenv:

    autoenv: ---(begin contents)---------------------------------------

    autoenv: sourcehomeubuntuvenvbinactivate

    autoenv:

    autoenv: ---(end contents)-----------------------------------------

    autoenv:

    autoenv: Are you sure you want to allow this? (yN) y

    (venv)> 可以看到提示符的变化进阶篇:pip高级用法

    命令自动补全

    pip支持自动补全功能,对于zsh用户非常友好。zsh用户使用如下命

    令就可以支持自动补全了:

    > pip completion--zsh>>~.zprofile

    > source~.zprofile

    或者在~.zshrc里面加一行:

    eval`pip completion--zsh`

    如果使用bash,则应该执行如下命令:

    pip completion--bash>>~.profile

    现在输入“pip i”就会变成“pip install”,自动补全了install这个

    子命令。

    普通用户安装

    如果不是root用户,没有sudo权限,也不在虚拟环境里面,要如何

    安装包呢?可以使用命令: > pip install django--user

    这样,安装的包会放在当前用户的.local目录下:

    > pip show django|grep Location

    Location:homeubuntu.locallibpython2.7site-packages

    不能在virtualenv中使用“--user”,需要退出虚拟环境后才可以使

    用。

    编辑模式

    作为开发者,为了让最新的开发版代码及时生效,开发过程中可以

    使用pip的编辑模式:

    > git clone https:github.comdongweimingvine

    > cd vine

    > pip install -e .

    > cd

    > python-c 'import vine;print vine'

    

    可以看到,“pip install-e .”会把开发目录作为包的路径。这样就可以

    实时修改了。使用devapi作为缓存代理服务器

    pip缓存只针对当前的用户。如果公司使用Python的规模很大,尤其

    是有很多自己分发的包的时候,使用缓存代理是非常提高下载效率的方

    法,这样就不再依赖网络环境到PYPI下载包了。

    > pip install devpi-server

    启动devpi-server:

    > devpi-server--host=0.0.0.0--start...

    starting background devpi-server at http:0.0.0.0:3141...

    默认缓存服务器使用了3141端口。现在我们就可以使用-i参数指定

    使用缓存代理了:

    > pip install-i http:localhost:3141rootpypitornado

    当然,可以把这个“index-url”设置写到配置文件中:

    > mkdir~.configpip-p

    > cat~.configpippip.conf

    [global]

    index-url=http:localhost:3141rootpypi+simple

    安装Django看一下: > pip install django

    Collecting django

    Downloading http:localhost:3141rootpypi+f70687b2d4a2dfa51Django-1.9.6-py2.py3

    -none-any.whl (6.6MB)

    100%|████████████████████████████████|6.6MB 42.1MBs

    Installing collected packages:django

    Successfully installed django-1.9.6

    可以看到现在的安装包是从缓存代理获取的,而不是从每次都到

    PYPI下载了。

    如果希望像PYPI那样有个Web界面,可以安装devpi-web:

    > pip install-U devpi-web

    重启devpi-server:

    > devpi-server--host=0.0.0.0--stop

    > devpi-server--host=0.0.0.0--start

    现在就可以通过http:127.0.0.1:3141访问这个简单的Web界面了。

    PYPI的完全镜像

    bandersnatch(https:bitbucket.orgpypabandersnatch)是PyPA组织

    根据PEP 381(http:www.python.orgdevpepspep-0381)实现的镜像客

    户端。它可以帮我们建立一个包含了全部包的本地镜像服务。先安装bandersnatch:

    (bandersnatch)> pip install-r https:bitbucket.orgpypabandersnatchrawstable

    requirements.txt

    (bandersnatch)> pip install zc.buildout

    (bandersnatch)> sudo apt-get install mercurial-yq

    (bandersnatch)> hg clone https:bitbucket.orgpypabandersnatch~bandersnatch

    (bandersnatch)> ~bandersnatch

    (bandersnatch)> buildout

    默认还没有创建bandersnatch.conf这个配置文件,假如不使用“-c”,默认是etcbandersnatch. conf。先启动bandersnatch创建配置文件:

    (bandersnatch)> bandersnatch-c bandersnatch.conf mirror

    更新directory配置项,修改directory选项的值为datapypi,这个目

    录就是存放包的根目录:

    (bandersnatch)> grep directory e|grep-v ';'

    directory=datapypi

    再次启动“bandersnatch mirror”:

    (bandersnatch)> sudo mkdirdata

    (bandersnatch)> sudo chown ubuntu:ubuntudata

    (bandersnatch)> bandersnatch-c bandersnatch.conf mirror

    现在就开始同步镜像了。需要注意,PYPI上所有的包加起来有几百

    GB,而且还在不断增加中,请合理安排硬盘资源。第3章 Flask Web开发

    Flask是非常流行的Python Web框架,它能如此流行,原因主要有如

    下几点:

    有非常齐全的官方文档,上手非常方便。

    有非常好的扩展机制和第三方扩展环境,工作中常见的软件都会有

    对应的扩展。自己动手实现扩展也很容易。

    社区活跃度非常高。

    微框架的形式给开发者更大的选择空间。

    Pocoo团队出品,Flask和相关依赖(Jinja2、Werkzeug)的设计很优

    秀。比如使用装饰器配置路由、用Blueprint实现模块化、请求应用

    上下文等。

    Flask主要依赖三个库。

    Jinja2:默认的模板引擎。

    Werkzeug:一个包含WSGI、路由、调试的工具集。

    Itsdangerous:基于Django签名模块(http:bit.ly28QV7Fb)的签名

    实现。

    Flask本身尽量保持了内核的精简,其设计初衷就是不会替开发者做

    太多决策,而且就算Flask已经帮你做出选择也能很容易地替换。举两个

    例子:

    Web程序不可避免要和数据库打交道,使用SQLAlchemy、MongoEngine、不用ORM (对象关系映射)而直接基于MySQL-python这样的底层驱动进行开发都是可以的,选择权完全在你的手

    中。

    把默认的Jinja2模板引擎替换成Mako或者其他模板引擎都非常容

    易。

    本章包含如下内容:

    通过多个应用例子了解Flask框架使用的一些精髓。

    介绍目前最流行的模板引擎Jinja2和Mako的使用,以及实践总结和

    做选择时的建议。

    通过一些例子让读者熟悉MySQLdb的使用,并演示如何与Flask应

    用集成。

    通过源码帮助读者理解Flask的上下文设计,并演示在大型应用中使

    用上下文钩子的例子。

    通过一个真实的案例学以致用。先分析需求,接着从零开始实现一

    个文件托管应用。Flask入门

    安装Flask

    我们先安装Flask:

    (venv)> pip install Flask

    为节省篇幅,之后“(venv)”这个前缀都省略掉。如无特殊说明,当前目录都是指homeubuntuweb_develop。

    从Hello World开始

    我们从最小的应用开始:

    1 coding=utf-8

    2 from flask import Flask

    3

    4 app=Flask(__name__)

    5

    6

    7 @app.route('')

    8 def hello_world: 9 return 'Hello World!'

    10

    11

    12 if__name__=='__main__':

    13 app.run(host='0.0.0.0', port=9000)

    启动它:

    > python chapter3section1hello.py

    Running on http:0.0.0.0:9000(Press CTRL+C to quit)

    打开浏览器,访问“http:127.0.0.1:9000”,就可以看到熟悉

    的“Hello World!”了。

    我们来深入地按行解析这段代码及其背后发生的事情。

    第1行,“ coding=utf-8”是声明Python源文件编码的语法。该编码信

    息后续会被Python解析器用于解析源文件。如果没有特殊的原因,应该统一地使用utf-8,而不要使用gb18030,gb2312等类型。为节

    省篇幅,之后的实例都不再写出这个声明。

    第2行,引入Flask类,Flask类实现了一个WSGI应用。

    第4行,app是Flask的实例,它接收包或者模块的名字作为参数,但

    一般都是传递__name__。让flask.helpers.get_root_path函数通过传入

    这个名字确定程序的根目录,以便获得静态文件和模板文件的目

    录。

    第7~9行,使用app.route装饰器会将URL和执行的视图函数的关系

    保存到app.url_map属性上。处理URL和视图函数的关系的程序就是

    路由,这里的视图函数就是hello_world。第12行,使用这个判断可以保证当其他文件引用这个文件的时候

    (例如“from hello import app”)不会执行这个判断内的代码,也就

    是不会执行app.run函数。

    第13行,执行app.run就可以启动服务了。默认Flask只监听虚拟机的

    本地127.0.0.1这个地址,端口为5000。而我们对虚拟机做的端口转

    发端口是9000,所以需要指定host和port参数,0.0.0.0表示监听所有

    地址,这样就可以在本机访问了。服务器启动后,会调用

    werkzeug.serving.run_simple进入轮询,默认使用单进程单线程的

    werkzeug.serving.BaseWSGIServer处理请求,实际上还是使用标准

    库Base-HTTPServer.HTTPServer,通过select.select做0.5秒的“while

    True”的事件轮询。当我们访问“http:127.0.0.1:9000”,通过

    app.url_map找到注册的“”这个URL模式,就找到了对应的

    hello_world函数执行,返回“Hello World!”,状态码为200。如果访

    问一个不存在的路径,如访问“http:127.0.0.1:9000a”,Flask找不到

    对应的模式,就会向浏览器返回“Not Found”,状态码为404。

    这里需要说明的是,默认的app.run的启动方式只适合调试,不要在

    生产环境中使用,生产环境应该使用Gunicorn或者uWSGI。

    其他的werkzeug自带类型还包括ThreadedWSGIServer和

    ForkingWSGIServer。

    如果想让服务停止,可以发送终止信号或者按Ctrl-C键。

    配置管理复杂的项目需要配置各种环境。如果设置项很少,可以直接硬编码

    进来,比如下面的方式:

    app=Flask(__name__)

    app.config['DEBUG']=True

    app.config是flask.config.Config类的实例,继承自Python内置数据结

    构dict,所以可以使用update方法:

    app.config.update(

    DEBUG=True,SECRET_KEY='...')

    app.config内置的全部配置变量可以参看Builtin Configuration

    Values(http:bit.ly28UUgW3)。如果设置选项很多,想要集中管理设

    置项,应该将它们存放到一个文件里面。app.config支持多种更新配置

    的方式。假设现在有个叫作settings.py的配置文件,其中的内容如下:

    A=1

    可以选择如下三种方式加载。

    1.通过配置文件加载。

    app.config.from_object('settings') 通过字符串的模块名字

    或者引用之后直接传入模块对象

    import settings

    app.config.from_object(settings)2.通过文件名字加载。直接传入文件名字,但是不限于只使用.py

    后缀的文件名。

    app.config.from_pyfile('settings.py', silent=True) 默认当配置文件不存在时

    会抛出异常,使用silent=True的时候只是返回False,但不会抛出异常

    3.通过环境变量加载。这种方式依然支持silent参数,获得路径后

    其实还是使用from_pyfile的方式加载。

    > export YOURAPPLICATION_SETTINGS='settings.py'

    app.config.from_envvar('SETTINGS')

    调试模式

    虽然app.run这样的方式适用于启动本地的开发服务器,但是每次修

    改代码后都要手动重启的话,既不方便也不够优雅。如果启用了调试模

    式,服务器会在代码修改后自动重新载入,并在发生错误时提供一个能

    获得错误上下文及可执行代码的调试页面。

    有两种途径来启用调试模式。

    1.直接在应用对象上设置

    app.debug=True

    app.run

    2.作为run的参数传入

    app.run(debug=True)需要注意,开启调试模式会成为一个巨大的安全隐患,因此它绝对

    不能用于生产环境中。

    Werkzeug从0.11版本开始默认启用了PIN(全称Personal

    Identification Number)码的身份验证,旨在让调试环境下的攻击者更难

    利用调试器。启动程序时可以看到类似的启动提示:

    > python chapter3section1debug.py

    Running on http:0.0.0.0:9000(Press CTRL+C to quit)

    Restarting with stat

    Debugger is active!

    Debugger pin code:146-867-947

    当程序有异常而进入错误堆栈模式,第一次点击某个堆栈想查看对

    应变量的值的时候,浏览器会弹出一个要求你输入这个PIN值的输入

    框。这个时候需要在输入框中输入146-867-947,然后确认,Werkzeug

    会把这个PIN作为cookie的一部分存起来(失效时间默认是8小时),失

    效之前不需要重复输入。而这个PIN码攻击者是无法知道的。

    当然,也可以使用指定PIN码的值:

    > WERKZEUG_DEBUG_PIN=123 python chapter3section1debug.py

    动态URL规则

    URL规则可以添加变量部分,也就是将符合同种规则的URL抽象成

    一个URL模式,如item1、item2、item3......假如不抽象,我们就得

    这样写: @app.route('item1')

    @app.route('item2')

    @app.route('item3')

    def item(id):

    return 'Item:{}'.format(id)

    正确的用法是:

    @app.route('item')

    def item(id):

    return 'Item:{}'.format(id)

    尖括号中的内容是动态的,凡是匹配到item前缀的URL都会被映

    射到这个路由上,在内部把id作为参数而获得。

    它使用了特殊的字段标记,默认类型是字符串。如

    果需要指定参数类型需要标记成这样的格式,converter有下面几种。

    string:接受任何没有斜杠“”的文本(默认)。

    int:接受整数。

    float:同int,但是接受浮点数。

    path:和默认的相似,但也接受斜杠。

    uuid:只接受uuid字符串。

    any:可以指定多种路径,但是需要传入参数。

    @app.route('')

    访问a和访问b都符合这个规则,a对应的page_name就是a。如果不希望定制子路径,还可以通过传递参数的方式。比

    如people?name=a,peo-ple?name=b,这样就可以通

    过“name=request.args.get('name')”获得传入的name值。

    如果使用POST方法,表单参数需要通过request.form.get('name')获

    得。

    自定义URL转换器

    Reddit可以通过在URL中用一个加号(+)隔开各个社区名字,方

    便同时查看来自多个社区的帖子。比如访

    问“http:reddit.comrflask+lisp”,就可以同时看flask和lisp两个社区的帖

    子。我们自定义一个转换器来实现这个功能,它还可以设置所使用的分

    隔符,不一定要用加号“+”。

    import urllib

    from flask import Flask

    from werkzeug.routing import BaseConverter

    app=Flask(__name__)

    class ListConverter(BaseConverter):

    def__init__(self, url_map, separator='+'):

    super(ListConverter, self).__init__(url_map)

    self.separator=urllib.unquote(separator)

    def to_python(self, value):

    return value.split(self.separator)

    def to_url(self, values):

    return self.separator.join(BaseConverter.to_url(value)

    for value in values)

    app.url_map.converters['list']=ListConverter

    @app.route('list1')

    def list1(page_names):

    return 'Separator:{}{}'.format('+', page_names)

    @app.route('list2')

    def list2(page_names):

    return 'Separator:{}{}'.format('|', page_names)

    这样我们访问“list1a+b”和“list2a|b”就能实现同样的功能了。自

    定义转换器需要继承至BaseConverter,要设置to_python和to_url两个方法。

    to_python:把路径转换成一个Python对象。

    to_url:把参数转换成为符合URL的形式。

    HTTP方法

    HTTP有多个访问URL方法,默认情况下,路由只回应GET请求,但是通过app.route装饰器传递methods参数可以改变这个行为:

    @app.route('login', methods=['GET', 'POST'])

    @app.route('jitem', methods=['DELETE', 'POST'])

    如果存在GET,那么也会自动地添加HEAD方法,无须干预。它会

    确保遵照HTTP RFC(描述HTTP协议的文档)(http:bit.ly2932IiA)处

    理HEAD请求,所以你完全可以忽略这部分的HTTP规范。从Flask 0.6

    起,它也实现了OPTIONS的自动处理。

    下面简要介绍HTTP方法和使用场景。

    GET:获取资源,GET操作应该是幂等的。

    HEAD:想要获取信息,但是只关心消息头。应用应该像处理GET

    请求一样来处理它,但是不返回实际内容。

    POST:创建一个新的资源。

    PUT:完整地替换资源或者创建资源。PUT操作虽然有副作用,但

    应该是幂等的。

    DELETE:删除资源。DELETE操作有副作用,但也是幂等的。

    OPTIONS:获取资源支持的所有HTTP方法。PATCH:局部更新,修改某个已有的资源。

    幂等表示在相同的数据和参数下,执行一次或多次产生的效果是

    一样的。

    唯一URL

    Flask的URL规则基于Werkzeug的路由模块。这个模块背后的思想

    是基于Apache以及更早的HTTP服务器的主张,希望保证优雅且唯一的

    URL。

    举个例子:

    @app.route('projects')

    def projects:

    return 'The project page'

    上述例子很像一个文件系统中的文件夹,访问一个结尾不带斜线的

    URL会被重定向到带斜线的规范的URL上去,这样也有助于避免搜索引

    擎索引同一个页面两次。

    再看一个例子:

    @app.route('about')

    def about:

    return 'The about page'URL结尾不带斜线,很像文件的路径,但是当访问带斜线的

    URL(about)会产生一个404“Not Found”错误。

    构造URL

    用url_for构建URL,它接受函数名作为第一个参数,也接受对应

    URL规则的变量部分的命名参数,未知的变量部分会添加到URL末尾作

    为查询参数。构建URL而不选择直接在代码中拼URL的原因有两点:在

    未来有更改的时候只需要一次性修改URL,而不用到处去替换;URL构

    建会转义特殊字符和Unicode数据,这些工作不需要我们自己处理。

    感受下面这个例子:

    from flask import Flask, url_for

    app=Flask(__name__)

    @app.route('item1')

    def item(id):

    pass

    with app.test_request_context:

    print url_for('item', id='1')

    print url_for('item', id=2, next='')

    test_request_context帮助我们在交互模式下产生请求上下文。执行它:

    > python chapter3section1url.py

    item1?id=1

    item1?id=2next=%2F

    跳转和重定向

    跳转(状态码301)多用于旧网址在废弃前转向新网址以保证用户

    的访问,有页面被永久性移走的概念。重定向(状态码302)表示页面

    是暂时性的转移。但是也不建议经常性使用重定向。在Flask中它们都是

    通过flask.redirect实现的:

    redirect(location) 默认是302

    redirect(location, code=301) 通过code参数可以指定状态码

    Flask还支持303、305、307重定向,但是较少被用到。

    基于前面所讲的内容,我们来看一个更全面的例子。首先是存放配

    置的config.py:

    DEBUG=False

    try:

    from local_settings import

    except ImportError:

    passlocal_settings.py文件是可选存在的,它不进入版本库。这是常用的

    通过本地配置文件重载版本库配置的方式。

    基于上面所讲的内容,我们看一个更复杂的应用(simple.py):

    1 from flask import Flask, request, abort, redirect, url_for

    2

    3 app=Flask(__name__)

    4 app.config.from_object('config')

    5

    6

    7 @app.route('people')

    8 def people:

    9 name=request.args.get('name')

    10 if not name:

    11 return redirect(url_for('login'))

    12 user_agent=request.headers.get('User-Agent')

    13 return 'Name:{0};UA:{1}'.format(name, user_agent)

    14

    15

    16 @app.route('login', methods=['GET', 'POST'])

    17 def login:

    18 if request.method=='POST':

    19 user_id=request.form.get('user_id')

    20 return 'User:{}login'.format(user_id)

    21 else:

    22 return 'Open Login page' 23

    24

    25 @app.route('secret')

    26 def secret:

    27 abort(401)

    28 print 'This is never executed'

    29

    30

    31 if__name__=='__main__':

    32 app.run(host='0.0.0.0', port=9000, debug=app.debug)

    这个例子有如下细节。

    第7行,访问people的请求会被301跳转到people上,保证了URL的

    唯一性。

    第12行,request.headers存放了请求的头信息,通过它可以获取UA

    值。

    第18行,request.method的值就是请求的类型。

    第27行,执行abort(401)会放弃请求并返回错误代码401,表示禁止

    访问。之后的语句永远不会被执行。

    第32行,能使用debug=app.debug是因为flask.config.ConfigAttribute

    在app中做了配置的代理,目前存在的配置代理项有:

    app.debug->DEBUG

    app.testing->TESTING

    app.secret_key->SECRET_KEY

    app.session_cookie_name->SESSION_COOKIE_NAME

    app.permanent_session_lifetime->PERMANENT_SESSION_LIFETIME app.use_x_sendfile->USE_X_SENDFILE

    app.logger_name->LOGGER_NAME

    上面例子中的app.debug其实就是app.config['DEBUG']。

    响应

    视图函数的返回值会被自动转换为一个响应对象,转换的逻辑如

    下:

    如果返回的是一个合法的响应对象,它会从视图直接返回。

    如果返回的是一个字符串,会用字符串数据和默认参数创建以字符

    串为主体,状态码为200,MIME类型是texthtml的

    werkzeug.wrappers.Response响应对象。

    如果返回的是一个元组,且元组中的元素可以提供额外的信息。这

    样的元组必须是(response, status, headers)的形式,但是需要至少包

    含一个元素。status值会覆盖状态代码,headers可以是一个列表或

    字典,作为额外的消息头。

    如果上述条件均不满足,Flask会假设返回值是一个合法的WSGI应

    用程序,并通过Response.force_type(rv, request.environ)转换为一个

    请求对象。

    下面的视图函数:

    @app.errorhandler(404)

    def not_found(error):

    return render_template('error.html'), 404可以改成如下显式地调用make_response的方式:

    @app.errorhandler(404)

    def not_found(error):

    resp=make_response(render_template('error.html'), 404)

    return resp

    第二种方法很灵活,可以添加一些额外的工作,比如设置cookie、头信息等。

    API都是返回JSON格式的响应,需要包装jsonify。可以抽象一下,让Flask自动帮我们做这些工作(app_response.py):

    from flask import Flask, jsonify

    from werkzeug.wrappers import Response

    app=Flask(__name__)

    class JSONResponse(Response):

    @classmethod

    def force_type(cls, rv, environ=None):

    if isinstance(rv, dict):

    rv=jsonify(rv)

    return super(JSONResponse, cls).force_type(rv, environ)

    app.response_class=JSONResponse

    @app.route('')

    def hello_world:

    return{'message':'Hello World!'}

    @app.route('custom_headers')

    def headers:

    return{'headers':[1, 2, 3]}, 201, [('X-Request-Id', '100')]

    if__name__=='__main__':

    app.run(host='0.0.0.0', port=9000)

    启动它之后,就可以在另一个终端看看自定义头信息的效果了。看

    效果之前,先安装httpie (https:github.comjkbrzthttpie):

    > pip install httpie

    httpie是一个使用Python编写的,提供了语法高亮、JSON支持,可

    以替代curl的工具,它也可方便地集成到Python项目中。本书绝大多数

    命令行下请求数据时都使用它。

    现在请求custom_headers:

    > http http:0.0.0.0:9000custom_headers

    HTTP1.0 201 CREATED

    Content-Length:44

    Content-Type:applicationjson

    Date:Thu, 26 May 2016 17:34:54 GMT Server:Werkzeug0.11.10 Python2.7.11+

    X-Request-Id:100

    {

    headers:[

    1,2,3

    ]

    }

    视图中也可以直接指定状态字符串,如使用'201 CREATED'替代数

    字的201。

    静态文件管理

    Web应用大多会提供静态文件服务以便给用户更好的访问体验。静

    态文件主要包含CSS样式文件、JavaScript脚本文件、图片文件和字体文

    件等静态资源。Flask也支持静态文件访问,默认只需要在项目根目录下

    创建名字为static的目录,在应用中使用“static”开头的路径就可以访

    问。但是为了获得更好的处理能力,推荐使用Nginx或者其他Web服务

    器管理静态文件。

    不要直接在模板中写死静态文件路径,应该使用url_for生成路径。

    举个例子:

    url_for('static', filename='style.css')生成的路径就是“staticstyle.css”。当然,我们也可以定制静态文件

    的真实目录:

    app=Flask(__name__, static_folder='tmp')

    那么访问“http:localhost:9000staticstyle.css”,也就是访

    问tmpstyle.css这个文件。

    即插视图

    即插视图的灵感来自Django的基于类而不是函数的通用视图方式,这样的视图就可以支持继承了。视图类型有两种类型。

    标准视图

    标准视图需要继承flask.views.View,必须实现dispatch_request。看

    一个例子(app_view.py):

    from flask import Flask, request, render_template

    from flask.views import View

    app=Flask(__name__, template_folder='....templates')

    class BaseView(View):

    def get_template_name(self):

    raise NotImplementedError

    def render_template(self, context):

    return render_template(self.get_template_name,context)

    def dispatch_request(self):

    if request.method !='GET':

    return 'UNSUPPORTED!'

    context={'users':self.get_users}

    return self.render_template(context)

    class UserView(BaseView):

    def get_template_name(self):

    return 'chapter3section1users.html'

    def get_users(self):

    return [{

    'username':'fake','avatar':'http:lorempixel.com100100nature'

    }]

    app.add_url_rule('users', view_func=UserView.as_view('userview'))

    if__name__=='__main__':

    app.run(host='0.0.0.0', port=9000)模板存放在~web_developtemplates下,使用__name__来获取模板

    目录,template_folder是相对于app.py文件的,需要设置

    成'....templates'才能找到正确的模板目录。

    基于调度方法的视图

    flask.views.MethodView对每个HTTP方法执行不同的函数(映射到

    对应方法的小写的同名方法上),这对RESTful API尤其有用。看一个

    例子(app_api.py):

    from flask import Flask, jsonify

    from flask.views import MethodView

    app=Flask(__name__)

    class UserAPI(MethodView):

    def get(self):

    return jsonify({

    'username':'fake','avatar':'http:lorempixel.com100100nature'

    })

    def post(self):

    return 'UNSUPPORTED!'

    app.add_url_rule('user', view_func=UserAPI.as_view('userview'))

    if__name__=='__main__':

    app.run(host='0.0.0.0', port=9000)

    通过装饰as_view的返回值来实现对于视图的装饰功能,常用于权

    限的检查、登录验证等:

    def user_required(f):

    def decorator(args,kwargs):

    if not g.user:

    abort(401)

    return f(args,kwargs)

    return decorator

    view=user_required(UserAPI.as_view('users'))

    app.add_url_rule('users', view_func=view)

    从Flask 0.8开始,还可以通过在继承MethodView的类中添加

    decorators属性来实现对视图的装饰:

    class UserAPI(MethodView):

    decorators=[user_required]

    蓝图

    蓝图(Blueprint)实现了应用的模块化,使用蓝图让应用层次清晰,开发者可以更容易的开发和维护项目。蓝图通常作用于相同的URL

    前缀,比如user:id、userprofile这样的地址,都以user开头,那么它们

    就可以放在一个模块中。看一个最简单的示例(user.py):

    from flask import Blueprint

    bp=Blueprint('user',__name__, url_prefix='user')

    @bp.route('')

    def index:

    return 'Users Index page'

    每个模块都会暴露一个全局变量bp。再看主程序(app_bp.py):

    from flask import Flask

    import user

    app=Flask(__name__)

    app.register_blueprint(user.bp)

    if__name__=='__main__':

    app.run(host='0.0.0.0', port=9000)

    使用register_blueprint注册模块,如果想去掉模块只需要去掉对应的

    注册语句即可。子域名

    现在许多SaaS应用为用户提供一个子域名来访问,可以借助

    subdomain来实现同样的功能(app_subdomain.py):

    from flask import Flask, g

    app=Flask(__name__)

    app.config['SERVER_NAME']='example.com:9000'

    @app.url_value_preprocessor

    def get_site(endpoint, values):

    g.site=values.pop('subdomain')

    @app.route('', subdomain='')

    def index:

    return g.site

    if__name__=='__main__':

    app.run(host='0.0.0.0', port=9000)

    在虚拟机上绑定一下域名,也就是在etchosts添加一行:

    127.0.0.1 a.example.com b.example.com

    现在验证它: > http http:b.example.com:9000--print b b表示只输出响应的主体

    b

    命令行接口

    在Flask 0.11之前,启动的应用的端口、主机地址以及是否开启

    DEBUG模式,都需要在代码中明确指定,一个比较好的方式是使用第

    三方扩展Flask-Script管理。从Flask 0.11开始, Flask集成了Click,现在

    可以直接在命令行直接执行flask命令启动Flask应用了:

    > export FLASK_APP=chapter3section1hello.py

    > export FLASK_DEBUG=1

    > flask run-h 0.0.0.0-p 9000

    这种命令行启动的方式要灵活得多,命令flask还支持shell子命令:

    > flask shell

    Python 2.7.11+(default, Apr 17 ......

您现在查看是摘要介绍页, 详见PDF附件(8400KB,838页)