Django、Tornado和flask是全栈网络框架,而Twisted更专注于网络底层的高性能封装,不提供HTML模版引擎等界面功能,因此不能称为全栈框架。
1、Django
发布于2003年,是当前python世界里最负盛名且最成熟的网络框架。相较于其他web框架,Django的功能是最完整的。
企业级开发框架
Django是遵循MVC架构的web开发框架,主要由以下几个部分组成:
管理工具:一套内置的创建站点、迁移数据、维护静态文件的命令工具。
模型:提供数据访问接口和模块,包括数据字段、元数据、数据关系等的定义及操作。
视图:Django的视图层封装了HTTP Request和Response的一系列操作和数据流,其主要功能包括URL映射机制、绑定模板等。
模版:是一套Django自己的页面渲染语言,用若干内置的tags和filters定义页面的生成方式。
表单:通过内置的数据类型和控件生成HTML表单
管理站:通过声明需要管理的model,快速生成后台数据管理网站。
2、Tornado
是一个强大的、支持协程、高效并发且可扩展的Web服务器,发布于2009年9月,应用于FriendFeed、Facebook等社交网站。Tornado的强项在于可以利用它的异步协程机制开发高并发的服务器系统。
3、Flask
是python web框架里比较年轻的一个,发布于2010年,核心功能简单,以扩展组件形式增加其他功能,因此被称为“微框架”。
flask是一个基于werkzeug和Jinja2的微框架,可扩展,让开发者自己选择用什么数据库插件存储他们的数据。
①内置开发服务器和调试器
网站程序调试是在将编制好的网站投入实际运行前,用手工或编译程序等方法进行测试,是修正语法错误和逻辑错误的过程。Flask自带的开发服务器使开发者在调试程序时无须再安装其他任何网络服务器。
Flask默认处于调试状态,使得运行中的任何错误会同时向两个目标发送信息,一个是python console,即启动python程序的控制台,另一个是HTTP客户端,即flask开发服务器将调试信息传递给了客户端。
②与python单元测试功能无缝衔接
Flask提供了一个与Python自带的单元测试框架unitest无缝衔接的测试接口,即Flask对象的test_client()函数,通过test_client()函数,测试程序可以模拟进行HTTP访问的客户端来调用Flask路由处理函数,并且获取函数的输出来进行自定义的验证。
③使用Jinja2模版
Flask通过使用Jinja2模版技术将HTML页面与后台应用程序联系起来,Jinja2是一个非常灵活的HTML模版技术,Jinja2模版使用配制的语义系统,提供灵活的模版继承技术,自动抗击XSS跨站攻击并且易于调试。
④完全兼容WSGI 1.0标准
WSGI具有很强的伸缩性且能运行于多线程或进程环境下,WSGI位于web应用程序与web服务器之间,与WSGI完全兼容使得Flask能够配置到各种大型网络服务器中。
⑤基于Unicode编码
Flask是完全基于Unicode的,HTTP本身是基于字节的,也就是说任何编码格式都可以在HTTP中传输,在默认情况下,Flask会自动添加一个UTF-8编码格式的HTTP head。
由于FLask本身是一个微框架,要开发一个完整的web应用通常还需要安装其他组件。SQLAlchemy和WTForm分别可以为Flask应用提供数据库访问及表单封装功能。
其中flask-wtf是对WTForm实现一个简单封装包,在它安装过程中会自动安装WTForm。
一个最简单的Flask程序:
分析:
(1)首先通过import语句引入Flask包中的Flask类
(2)之后建立一个Flask类的实例APP。应该把应用模块或包的名字传给Flask构造函数的第一个参数,Flask在运行过程中将使用这个参数作为定位模版和其他静态文件的基础。__name__是python语言的一个内置属性,它包含的内容指明了.py文件的调用方式。本例中将__name__传给Flask,这样可以在用两种方式调用本模块时都能使代码工作:直接运行本模块时,__name__的值为__main__;通过其他模块调用本模块时,__name__的值为本模块的名称。
(3)使用route()装饰器告诉Flask紧跟着函数装载在哪个URL地址中。本例中,HTTP的根地址被装载为hello_flask()函数,如果需要把函数装载在其他URL地址中,则只需要修改route()参数如app.route(‘/say/hello’)即可达到目标。
(4)可以在装载函数中直接返回要作为内容传给HTTP客户端的数据,可以是字符串或者是HTML、XML、JSON消息体。本例中直接返回“Hello, World!”给客户端。
(5)if __name__=='__main__'语句用于指示当本模块被直接启动时才运行其作用于中的代码,在其作用域中执行app.run()进入Flask消息循环。
通过运行上述代码
现在已经完成并启动了一个Flask程序,该程序挂载在127.0.0.1的端口5000上,它的功能是当访问http://127.0.0.1:5000/时,程序会返回欢迎词:“Hello, World!”
程序只运行在本地地址127.0.0.1上,这是Flask处于安全考虑而执行的默认行为。因为Flask启动时默认是出于调试模式的,所以在运行中如果发生错误会直接返给客户端。所以,Flask以默认的启动方式监听本地的127.0.0.1地址,不允许外部客户端访问。
当确定系统可以接受来自外部的访问时,可以通过给run()方法设置参数的方式来实现。同时,在run参数中可以传入要监听的端口,并且设置是否处于调试模式运行。如果将helloworld.py文件中的app.run()改为如下命令,则系统将会监听所有地址的80端口,并关闭调试模式。
模版渲染
在现实网站建设中,网站服务需要使用HTML与浏览器进行交互,用python生成HTML代码不是一件轻松的事,所以,Flask使用Jinja模版引擎实现了自动模版渲染功能。Flask的Jinja2模版特性通过加载HTML模版文件,并将其嵌入必要的参数和逻辑来达到简化HTML输出的目的。
①用render_template实现模版渲染
模版渲染通过Flask包的render_template方法实现。通过要把加载的模板文件和参数传给该方法,即可实现HTML的自动渲染。
一个函数可以通过多个route()装饰器绑定到多个URL上。
在本例子中,render_template加载hello.thml模板文件,并把name参数传给该模板,本例中只把模板文件的文件名传给了render_template()函数,并没有指定该文件的路径,那么Flask是到网站的templates目录中加载该文件。假设python代码被保存在application.py中,则本例的网站目录结构如下:(模板文件必须被保存在网站的/templates目录下)
模板文件中包含静态HTML内容、变量、控制逻辑。下面是hello.html文件的内容:
结合,application.py和hello.html的内容,当客户端访问http://127.0.0.1:5000/hello时,服务器将返回HTML块:
当客户端访问http://127.0.0.1:5000/hello/David时,服务器会返回HTML块:
②用Markup转换变量中的特殊字符
向render_template传入的参数,不仅可以是单纯的字符串,还可以包含HTML特殊字符(比如<、>、空格、/等),这给模板参数提供了更好的灵活性。同时,由于这些特殊字符会被HTML客户端解释成特殊含义,因此会给网站程序带来一定程度的安全隐患。Flask允许程序员自己控制Jinja2是否需要解释这些特殊字符,如果这些字符应该被解释成特殊含义,则将这些参数直接传给tender_template即可,如果这些字符仅应该被解释成字符串,则应该通过markup()函数将这些字符串做转义处理,然后传给render_template()函数。
重定向和错误处理
重定向是指将一个网络请求重新指定URL并转到其他地址的技术。Flask的redirect()函数提供了这个功能。此外,如果仅仅想中止一个请求并返回错误,而不是重定义向其他地址,则可以使用abort()函数。
本例中,当客户端访问根页面时,处理函数index()通过redirect()函数将请求重定向到了/check页面,而/check页面中目前没有实现其他逻辑,仅仅向客户端返回了400错误。
本例中flask的errorhandler装饰器添加了自定义的错误处理器。当程序中返回400错误时,系统会执行bad_request()函数。在该函数中向客户端返回400错误的同时,还传送了自定义的错误页面bad_request.html。
路由详解
带变量的路由
如果要使用URL中包含可变的部分,则可以铜鼓哦在需要的URL部分添加变量还实现,这个变量会作为参数传递给路由所关联的python函数。
①在路径中添加变量
可以直接在路径中通过<variable_name>的方式添加变量,并应用在被映射的函数中:
被添加的参数需要两次被声明:第一次是在route()装饰器的参数中,在需要使用变量的URL部分用<variable_name>方式声明变量;第二次是在所映射的函数(本例中为show_welcome)的参数部分声明变量名,这样被声明的变量就可以在映射函数内使用了。本例中username变量被作为欢迎语句的一部分回传给客户端。
②为变量指定类型
本例中的变量number在第一次被声明时被映射为int类型,如果不指定,则变量会被映射为默认的path类型。
③路径最后的分隔符的作用
在URL路径中 ,“/”被用作为路径分隔符,当它被写在URL路径的开头时,则表明本路径是一个绝对路径,当它被写在路径中间时,它被用作隔离路径的层级,那么当它被写在最后时,作用如下:
上述的例子中,声明并挂载了两个路径,非常相似,只是第一个路径的最后有“/”分隔符,它看上去像一个文件系统的目录;第二个路径的最后没有“/”分隔符,看上去像一个类unix文件系统的文件名。因此,两种方式对它们的访问效果有所不同,有“/”结尾的路径样式除了可以接受对其本身的访问,还可以接受相同路径前缀但不带“/”结尾的路径访问,而不带“/”结尾的路径样式则没有此效果。
HTTP方法绑定
网站通过HTTP与浏览器或其他客户端进行交互,而HTTP访问一个URL时,可以使用几种不同的访问方式,包括GET、POST、HEAD、DELETE等,在flask中,路由默认设置使用GET方式进行路径访问。
①指定HTTP访问方式的方法
上述中,在route装饰器中显示地声明了两种HTTP访问方式:get和post,无论客户端使用哪种方式访问地址/SendMessage,flask都会定位到Messaging()函数并执行。可以在函数中通过request.method属性获得本次HTTP请求的访问方式。
②将同一个URL根据访问方式映射到不同的函数
路由地址反向生成
有时,程序中需要通过函数名称获得与其绑定的URL地址,Flask通过url_for()函数实现这个功能。
test_request_context()方法用于告诉解释器在其作用域中的代码模拟一个HTTP请求上下文,使其好像被一个HTTP请求所调用,HTTP请求上下文是调用url_for()函数所必需的环境。
使用context上下文
会话上下文(Session Context)是 Web 服务器上基于 Cookie 的对象,它提供了为同一个客户端在多次请求之间共享信息的方式。应用全局对象(Application Global)提供了在一次请求的多个处理函数中共享信息的方式。请求上下文(Request Context)是 Web 服务器管理单次用户请求的环境对象,用于处理客户端向 Web 服务器发送的数据。
会话通常通过Cookie实现,基本原理如下:
(1)在服务器收到客户端的请求时,检查该客户端是否设置了标识SessionID的Cookie。如果不存在SessionID或者SessionID无效,则认为该请求是一个新的会话。
(2)当服务器端识别到新的会话时,生成一个新的SessionID并通过Cookie传送给客户端。
(3)客户端在下一次请求时提交在之前获得的SessionID,此时服务器认为该请求与之前生成SessionID的请求属于同一个会话。
本例中的writeSession()函数将当前时间写入会话的键值key_time中,在readSession中将其读出,分别访问这两个函数的映射地址,其结果为同一个时间值。
除了进行正常的数据保存和读取,flask.session对象还维护自身的状态,这通过如下两个属性来实现。
new:判断本次请求的Session是否是新建的
modified:判断本次请求中是否修改过session键值
②应用全局对象
在Flask中每个请求可能会触及多个响应函数,而如果想要在多个响应函数之间共享数据,则需要用到应用全局对象,应用全局对象是Flask为每个请求自动建立的一个对象。相对于简单的全局对象,应用全局对应可以保证线程安全,通过flask.g来实现,可以在其中保存开发者需要的任何数据,数据库连接时一个典型的应用。
在请求中第一次需要使用数据库时,用数据库的字符串建立数据库对象并连接
在同一个请求后的任何需要用到数据库的操作中,使用前面已经建立的数据库连接进行数据库操作
当请求完成时,框架自动关闭数据库连接,有效地使用系统资源。
get_db()函数用于从flask.g中获得数据库连接对象。如果在flask.g中找不到数据库对象,则建立一个新的数据库连接,并且保存在flask.g中以便下次使用。由于应用了装饰器teardown_request(),因此teardown_db()函数在请求结束时自动被flask框架调用。代码在teardown_db()函数中检查本次请求时否连接过数据库,如果有则将其关闭以释放资源。
上述代码分别在验证登录信息和查询数据处调用了get_db()。第一次调用get_db()时,get_db()会创建数据库连接;第二次调用get_db()时,则直接复用之前建立的连接。在代码中无须显式地调用teardown_db()以释放资源,因为Flask框架会自动调用它。
请求上下文
①访问URL参数和路径
在“http://localhost/testurl?next=http://example.com/&testdata=abc”
URL参数包括“next=http://example.com/”和“testdata=abc”,URL的根路径是“http://localhost/testurl”
访问“http://localhost/redirect_url?next=http://localhost/echo_url”时,redirecrt_url()函数被调用,通过request.args属性获得URL参数next,然后通过redirect重定向到next中的地址。在echo_url()中,函数通过request.base_url属性获得本次访问的路径并返回“http://localhost/echo_url”。
Jinja2模板编程
Jinja2语法
Jinja2模板可以保存在任何基于文本的文件中,比如XML、HTML、CSV等,所以模板文件本身可以接受任何文件后缀,比如.html、.xml等。
Jinja2模板由普通内容、变量、表达式、标签和注释组成。
普通内容:为没有特殊含义的内容,渲染模板时不对其进行解释。
变量:在Jinja2中可以定义变量,在渲染模板时变量会被替换为其包含的值
表达式:可以针对变量进行算术或逻辑操作
标签:用于渲染模板时进行逻辑控制
注释:渲染模板时会将注释内容从生成的文件中删除
Jinja2的Hello World模板例子:
其中大多数HTML标签(如<head>、<body>、<li>)为普通内容,另外包括通过特殊格式定义的内容,如下:
使用过滤器
在Jinja2中过滤器时一种转换变量输出内容的技术,比如将字符串变量转换为大写形式、在其中去除特别字符等。
过滤器通过管道符号“|”与变量连接,并且可以通过圆括号传递参数。举例说明:
解释:在本条语句中,my_variable是待转换的变量,default是过滤器,my_variable is not defined是过滤器的参数。default过滤器的含义是:判断被转换的变量是否被定义过,如果没有被定义过,则用字符串参数替换被转换的变量。
常用的过滤器:
循环语句
例如,要显式一个由users变量提供的用户列表
模板继承
①基模板
②子模板
既然基模板是一个完整的HTML文件,那么子模板就无须再重复基模板中已有的内容了,只需要声明自己继承自哪个基模板,并且定义基模板中尚未定义的块。
渲染结果:
SQLAlchemy数据库编程
WTForm表单编程
4、Twisted
是一个有着近二十年历史的开源事件驱动框架,不像前3种着眼于网络HTTP应用的开发,而是适用于从传输层到自定义应用协议的所有类型的网络程序的开发,并能在不同的操作系统上提供很高的运行效率。