一、初始化
每个Flask程序都必须创建一个实例,即Flask类的对象,一般采用如下形式:
1 from flask import Flask
2
3 app = Flask(__name__)
其中, name用于指定程序主模块或包的名字,Flask利用name的值决定程序所在根目录,以方便相对于根目录来查找资源文件。
二、路由
1、静态路由
当客户端发出请求给web服务器后,web服务器会将该请求转发给Flask实例进行处理,因此Flask实例必须知道每个接收到的请求该对应运行那个接口,这就通过保存URL地址到接口的映射关系来实现路由。 Flask通过app.route装饰器将函数注册为路由,如:
1 @app.route('/')
2 def hello_world():
3 return 'Hello World!'
代码中,将hello_world()函数注册为程序根地址的处理接口,假设当前程序的域名为"www.example.com",则在浏览器中输入"http://www.example.com/"后,将输出"Hello World!",此即Flask程序给出的响应。
2、动态路由
上面的路由称为静态路由,即路由地址是固定的,但有些应用场景下,我们可能会发现路由地址中有些部分是可变的,比如获取用户信息的URL地址"http://www.example.com/pattywgm"中pattywgm就是可变的部分,对不同的用户对应不同的用户编号。Flask为我们提供了参数式的可变路由方式,如:
1 @app.route("/user/<user_name>")
2 def hello_user(user_name):
3 return "Hello %s" % user_name
三、启动服务器
Flask程序以run()方法启动web服务器,如:
1 if __name__ == '__main__':
2 app.run()
服务器启动后,会进入轮询,一直运行到程序停止为止。开发中,我们可以设置app.run(debug=True)来开启debug模式,方便调试。
四、运行示例
Flask默认启用5000端口
五、程序上下文
Flask接收到来自客户端的请求后,路由到指定的接口进行响应处理并返回处理结果。响应接口需要知道客户端的请求体,即request对象才能进行正确的处理。如果给每个接口函数都传递一个request对象参数,太过冗余,且代码会很难看,因此Flask使用上下文临时将某些对象编程线程内的全局变量,即在同一个线程内,上下文信息可全局共享,且不会干扰到其他的线程环境。
Flask有两种上下文,分别为程序上下文和请求上下文,各自对应的全局变量如下表:
变量名 上下文 备注
current_app 程序上下文 表示当前运行的程序实例
g 程序上下文 处理请求时用作临时存储对象,每次请求都会重新设值
request 请求上下文 客户端发来的request请求对象
session 请求上下文 请求携带的会话信息
Flask在分发请求之前会激活上下文信息,在请求处理完成后再删除。上下文只有在激活后,才可使用对应的全局变量,否则会报异常。
app_ctx = app.app_context() #可获取app对应的上下文
app_ctx.push() #推送上下文信息,即激活上下文
六、请求钩子
有时候,在请求开始或结束后,我们可能想要执行一些代码,比如,在请求开始前,完成数据库的连接,用户信息的认证等等。Flask使用装饰器为我们提供了注册通用函数的功能,支持的钩子函数有以下四种:
before_first_request 第一次请求之前调用
before_request 每次请求之前调用
after_request 每次请求之后调用,前提是没有未处理的异常抛出
teardown_request 每次请求之后调用,即使有未处理的异常抛出
在请求函数和钩子函数之间,一般通过全局变量g实现数据共享。
七、响应
Flask的响应一般可包含三个参数,第一个参数指明响应的字符串,第二个参数可指明响应状态码,如200表示响应成功,第三个参数是由header首部组成的字典。我们可以通过make_response()生成一个response对象并设置。
1 #!/usr/bin/env python
2 # encoding: utf-8
3 from flask import Flask
4 from flask import request
5
6 app = Flask(__name__)
7
8
9 @app.route('/')
10 def hello_world():
11 return 'Hello World!'
12
13
14 @app.route("/user/<user_name>")
15 def hello_user(user_name):
16 """
17 动态路由示例
18 :param user_name: 用户名
19 :return:
20 """
21 1/0
22 return "Hello %s" % user_name
23
24
25 @app.route("/context")
26 def get_context():
27 """
28 获取请求的上下文信息
29 :return:
30 """
31 user_agent = request.headers.get("user-agent")
32 return "your user-agent is %s" % user_agent
33
34
35 @app.before_first_request
36 def first_quest():
37 print "run before first request"
38
39
40 @app.before_request
41 def every_request():
42 print "One new request is comming"
43
44
45 @app.after_request
46 def after_r(exception):
47 print "The request has done 1"
48
49 @app.teardown_request
50 def teardown_r(exception):
51 print "The request has done 2"
52
53
54 if __name__ == '__main__':
55 app.run()
上述代码运行后,在浏览器分别请求
http://127.0.0.1:5000/ 输出:
run before first request
One new request is comming The request has done 1 The request has done 2
http://127.0.0.1:5000/user/patty 输出:
One new request is comming
The request has done 2
变量共享:通过g实现
1 @app.route('/')
2 def hello_world():
3 print g.user
4 return 'Hello World!'
5
6 @app.before_first_request
7 def first_quest():
8 g.user = 'tmp'
9 print "run before first request"
八、引入JinJa2模版
首先我们来对比两段代码,它们实现同样的功能, 都是现实用户信息列表:
1 # 第一段
2 @app.route("/users")
3 def get_users():
# 前面省略逻辑处理过程,得到users列表
4 users = [{"name": "Jack", "age": 25, "city": "NewYork"},
5 {"name": "Rose", "age": 26, "city": "Beijing"},
6 {"name": "LiLi", "age": 23, "city": "ShangHai"}]
7 html = """
8 <table border="1">
9 <tr>
10 <th>姓名</th>
11 <th>年龄</th>
12 <th>城市</th>
13 </tr>"""
14 for user in users:
15 h = """<tr>
16 <td>%s</td>
17 <td>%s</td>
18 <td>%s</td>
19 </tr>"""%(user['name'], user['age'], user['city'])
20 html += h
21 html += "</table>"
22 return html
23
24 # 第二段
25 @app.route("/users")
26 def get_users_with_tempalte():
# 逻辑处理
27 users = [{"name": "Jack", "age": 25, "city": "NewYork"},
28 {"name": "Rose", "age": 26, "city": "Beijing"},
29 {"name": "LiLi", "age": 23, "city": "ShangHai"}]
30 return render_template("users.html", users=users)
第一段代码中,将业务逻辑处理与视图展现的html都放在视图函数里,当业务处理逻辑或展现页面较复杂时,整个试图函数将会非常丑陋,难以理解且不易维护。
1 <table border="1">
2 <tr>
3 <th>姓名</th>
4 <th>年龄</th>
5 <th>城市</th>
6 </tr>
7
8 {% for user in users %}
9 <tr>
10 <td>{{ user.name }}</td>
11 <td>{{ user.age }}</td>
12 <td>{{ user.city }}</td>
13 </tr>
14 {%endfor%}
15
16 </table>
第二段代码中, 我们将业务逻辑处理与页面展现分开,将展现逻辑转移到模版中,使得代码目的清晰,层次分明,而且在模版中我们可以更好的进行页面渲染,这就是Jinja2模版引擎的魅力。
render_template()函数用于渲染模版,第一个参数表示模版文件名, 我们需要在当前项目路径下创建一个templates文件夹,用来放置html模版文件。随后的参数都是键值对,表示模版中的变量对应的实际值。
九、Jinja2模版中的控制结构
1、if-else语句
{% if name %}
<h1>Hello, {{ name }}!</h1>
{% else %}
<h1>Hello, Stranger!</h1>
{% endif %}
2、for循环(见(八)中示例)
3、宏定义
Jinja2中的宏,类似Python中的函数,可供需要时调用,例如对于对于上文展现用户列表的例子,我们可以这样定义:
1 {% macro render_user(user) %}
2 <td>{{ user.name }}</td>
3 <td>{{ user.age }}</td>
4 <td>{{ user.city }}</td>
5 {% endmacro %}
6
7 <table border="1">
8 <tr>
9 <th>姓名</th>
10 <th>年龄</th>
11 <th>城市</th>
12 </tr>
13
14 {% for user in users %}
15 <tr>
16 {{ render_user(user) }}
17 </tr>
18 {%endfor%}
19
20 </table>
其中:macro表示定义宏, render_user相当于函数名, user即为参数。若是某些宏重复使用率较高, 我们可以将它们保存在一个单独的html文件中,然后通过 {% import 'macros.html as macros %}引入,类似于Python的模块调用过程。
4、模版引用
和宏的引用类似, 我们可将多处重复使用的模版代码保存在单独的文件中,然后在需要的地方引用即可{% include 'common.html' %}
5、模版继承
模版继承类似于Python的继承,我们可以创建一个base.html模版,该模版包含基本的html块结构,通过继承该模版,可省去在每个模版中都书写相似内容的过程,而只突出当前模版的主要功能。
<!--base.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Jinja2 Template</title>
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
<!-- users.html -->
{% extends 'base.html' %}
{% block body %}
{% macro render_user(user) %}
<td>{{ user.name }}</td>
<td>{{ user.age }}</td>
<td>{{ user.city }}</td>
{% endmacro %}
<table border="1">
<tr>
<th>姓名</th>
<th>年龄</th>
<th>城市</th>
</tr>
{% for user in users %}
<tr>
{{ render_user(user) }}
</tr>
{%endfor%}
</table>
{% endblock %}
extends关键字表示继承关系, {% block body %}...{% endblock %}实现了body部分内容的个性化展现。
|
|