Flask是一个基于Werkzeug和Jinja2的微框架。微框架意味着Flask的核心非常简单。Flask于2010年出现,这使得它吸收了其他框架的优点,并且把自己的主要领域定义在了微小项目上(Flask是一个面向简单需求和小型应用的微框架),同时具有很强的扩展功能。不像Django,它不会为开发者做太多技术决定,Flask可以自由的选择任何模板引擎或ORM。即使它带有默认的Jinja2模板引擎,开发者仍然可以选择自己喜欢的其他引擎。
- 内置开发服务器和调试器
- 与Python单元测试功能无缝衔接
- 使用Jinja2模板
- 完全兼容WSGI1.0标准
- 基于Unicode编码
这里我建立了虚环境来完成所有基于Flask的项目。
- 安装虚环境
pip install virtualenv
- 使用虚环境
cd [使用该虚环境的目录]
virtualenv venv
该条命令执行后,将在这个目录中建立一个venv目录,该目录复制了一份完整的当前系统的Python环境(注意:如果系统中安装了多个Python版本,可以在该命令中使用-p参数指定从哪个版本的Python复制虚拟环境)。
- 在当前虚环境下安装Flask相关组件
进入/venv/Scripts/环境下> pip install flask
进入/venv/Scripts/环境下> pip install sqlalchemy flask-wtf
- 其他虚环境下相关命令示例
- 用虚环境运行Python程序
进入/venv/Scripts/环境下> python xxxx.py
- 用activate命令启动虚环境(之后便不必再显式的调用虚环境bin文件夹中的命令)
进入/venv/Scripts/环境下> activate
- 用deactivate命令退出虚环境
deactivate
- Sublime Text使用Python虚环境里的包运行Python程序
参考这里。此处不再赘述。
创建hello_flask.py
程序:
'''
1. 导包
'''
from flask import Flask
'''
2. 建立一个Flask类的实例app
2.1 应用模块或包的名字传给Flask构造函数的第一个参数,
Flask在运行过程中将使用这个参数作为定位模板和其他静态文件的基础。
2.2 __name__是Python语言的一个内置属性,它包含的内容指明了.py文件的调用方式。
本例中将__name__传递给Flask,这样可以在用两种方式调用本模块时都能使代码工作:
a. 直接运行本模块时,__name__的值为__main__;
b. 通过其他模块调用本模块时,__name__的值为本模块的名称。
'''
app = Flask(__name__)
'''
3. 使用route()装饰器告诉Flask紧跟着函数装载在哪个URL地址中。
本例中,HTTP的根地址被装载为hello_flask()函数。
如果需要把函数装载在其他URL地址中,则只需要修改route()参数,
如app.route('/say/hello')即可达到目标。
'''
'''
4. 在装载函数中直接返回'Hello, Flask!'内容传给HTTP客户端,
除可以为字符串外,返回的内容还可以是HTML、XML、JSON消息体。
'''
@app.route('/')
def hello_flask():
return 'Hello, Flask!'
'''
5. if __name__ == '__main__'语句用于指示:
当本模块被直接启动时才运行其作用域中的代码。
这里作用域中的app.run()进入Flask消息循环体,
app.run()将一直运行,不要在其后面再写任何代码。
'''
if __name__ == '__main__':
app.run()
运行效果如下:
现在已经完成了一个Flask程序,该程序挂载在127.0.0.1的5000端口上。它的功能是当访问http://127.0.0.1:5000/时,程序会返回“Hello, Flask!”:
为什么程序只运行在本地地址127.0.0.1上呢?这是Flask出于安全考虑而执行的默认行为。因为Flask启动时默认是处于调试模式的,所以在运行中如果发生错误会直接返回给客户端,让陌生人知道这些错误信息会威胁到网站的安全性,所以Flask以默认的启动方式监听本地的127.0.0.1地址,不允许外部客户端访问。但是,可以通过run()方法提供来自外部的访问:在run()参数中可以传入要监听的端口,并且设置是否处于调试模式运行。例如:
app.run(host='0.0.0.0', port=80, debug=False)
功能:系统将会监听所有地址的80端口并关闭调试模式。
Flask使用Jinja模板引擎实现了自动模板渲染功能。Flask的Jinja2模板特性通过加载HTML模板文件,并在其中嵌入必要的参数和逻辑来达到简化HTML输出的目的。
把要加载的模板文件和参数传给Flask包的render_template方法,即可实现HTML的自动渲染。向render_template传入的参数,不仅可以是单纯的字符串,还可以包含HTML特殊字符(比如<>、空格、/等),这些特殊字符会被HTML客户端解释成特殊含义(这会给网站程序带来一定程度而安全隐患,解决方式即为接下来要讲解的Markup方法)。
from flask import render_template
@app.route('/hello')
@app.route('/hello/' )
def hello(name=None):
# render_template加载hello.html模板文件,并把name参数传给该模板
return render_template('hello.html', name=name)
如果希望传入HTML的特殊字符,但又不想要其被解释成特殊含义,而仅仅是作为字符串,则应该通过Markup()函数将这些字符串做转义处理,然后传给render_template函数。
redirect()
:将一个网络请求重新指定URL并转到其他地址。
abort()
:终止一个请求并返回错误(不重定向到其他地址)。
在上面第1节的学习中,我们将根URL路由绑定在一个输出‘Hello Flask!’字符串的函数中。接下来将学习如何配置更复杂的带参数的路由。
如果要使URL中包含可变的部分,则可以通过在需要的URL部分添加变量来实现,这个变量会作为参数传递给路由所关联的Python函数。
@app.route('/login/' )
def hello(name=username):
return 'Hello %s' % username
@app.route('/add/' )
def add_one(num):
return '%d' % (num+1)
在URL路径中,“/”被用作路径分隔符。当它被写在URL路径的开头时,则表明本路径是一个绝对路径;当它被写在路径中间时,它被用作隔离路径的层级;当它被写在路径最后时,它既接收对其本身的访问,也可以接受相同路径前缀但不带“/”结尾的路径访问(如果一个路径最后没有写“/”,则它只能接受对其本身的访问,而不能接受相同路径前缀但带“/”结尾的路径访问。也就是说,带“/”结尾的路径的接受程度更高)。
声明方式举例 | 访问方式举例 | 能否访问到 |
---|---|---|
@app.route(’/school/’) | http://localhost/school/ | 可以 |
http://localhost/school | 可以 | |
@app.route(’/student’) | http://localhost/student/ | 不可以 |
http://localhost/student | 可以 |
网站通过HTTP与浏览器或其他客户端进行交互,而HTTP访问一个URL时可以使用不同的访问方式,如GET、POST、HEAD、DELETE等。在Flask中,路由默认设置使用GET方式进行路径访问。
@app.route('/SendMessage', methods=['GET', 'POST'])
def Messaging():
if request.method == 'POST':
do_send()
else:
show_the_send_form()
'''
request是Flask框架的一个全局对象,可以获得很多和HTTP请求的客户端相关的信息。request.method属性获得本次HTTP请求的访问方式。
'''
@app.route('/Message', methods=['POST'])
def do_send():
return "This is for POST methods"
@app.route('/Message', methods=['GET'])
def show_the_send_form():
return "This is for GET methods"
注意:由于HTTP的不同访问方式之间存在着关联,所以Flask定义了两组隐式的访问方式的设置规则——
- 当在route装饰器中GET方式被指定时,HEAD方式也会被自动加入该装饰器中。
- 在Flask 6.0 之后,当在route装饰器中有任意方式被指定时,OPTIONS访问方式也会被自动加入该装饰器中。
前面讲解的都是将URL绑定到映射函数的方法,下面,我们通过函数名称获得与其绑定的URL地址。Flask通过url_for()
函数实现了这个功能:
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/')
def f_root():
pass
@app.route('/industry')
def f_industry():
pass
@app.route('/book/' )
def f_book(book_name):
pass
with app.test_request_context():
print(url_for('f_root'))
print(url_for('f_industry'))
print(url_for('f_industry', name='web'))
print(url_for('f_book', book_name='Python Book'))
'''
讲解1.
url_for(需要获取URL的函数名, [对URL中的变量赋值])
讲解2.
test_request_context()方法用于告诉解释器在其作用域中的代码模拟一个HTTP请求上下文,
使其好像被一个HTTP请求所调用。
HTTP请求上下文是调用url_for()函数所必须的环境,下一节将进行学习。
'''
%20
。上下文(Context)是Web编程中的一个相当重要的概念,它是在服务器端获得应用及请求相关信息的对象。
基于Cookie对象的会话的实现原理:
- 在服务器收到客户端的请求时,检查客户端是否设置了表示SessionID的Cookie。如果不存在SessionID或者SessionID无效,则认为该请求是一个新的会话。
- 当服务器识别到新的会话时,生成一个新的SessionID并通过Cookie传送给客户端。
flask.session
对象操作会话的实例:from flask import Flask, session
from datetime import datetime
app = Flask(__name__)
app.secret_key = 'SET_ME_BEFORE_USE_SESSION'
'''
将当前时间写入会话的键值key_time中
'''
@app.route('/write_session')
def writeSession():
session['key_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
return session['key_time']
'''
读取上次调用writeSession()时写入的时间,并返回
'''
@app.route('/read_session')
def readSession():
return session.get('key_time') or 'None'
if __name__ == '__main__':
app.run()
flask.g
进行实现(这是一个管理应用全局对象的实例):from flask import g
class MyDB():
def __init__(self):
print('A db connection is created...')
def close(self):
print('A db is closed...')
def connect_to_db():
return MyDB()
def get_db():
# 从flask.g中获得数据库连接对象
db = getattr(g, '_database', None)
# 若没有从flask.g中获得数据库连接对象,则建立一个新的数据库连接,并将其保存在flask.g中
if db is None:
db = connect_to_db()
g._database = db
return db
'''
应用装饰器@app.teardown_request,
从而使得teardown_db()函数在请求结束时自动被Flask框架调用。
'''
@app.teardown_request
def teardown_db(response):
db = getattr(g, '_database', None)
if db is not None:
db.close()
可以在请求处理函数的任何地方调用get_db()
。下面的代码实例分别在验证登录信息和查询数据处调用了get_db()
:
from flask import Flask
app = Flask(__name__)
def login():
db = get_db()
session['has_login'] = True
@app.route('/view_list')
def view_list():
if 'has_login' not in session:
login()
db = get_db()
return "teardown_db() will be called automatically"
from flask import request, url_for, redirect
app = Flask(__name__)
@app.route('/redirect_url')
def redirect_url():
next = request.args.get('next') or url_for('index')
return redirect(next)
@app.route('/echo_url')
def echo_url():
return request.base_url
代码分析:在访问http://127.0.0.1:5000/readirect_url?next=http://127.0.0.1:5000/echo_url
时(URL参数为next=http://127.0.0.1:5000/echo_url
,根路径为http://127.0.0.1:5000/readirect_url
),readirect_url()
函数被调用,它通过request.args
属性获取URL参数next,然后通过redirect
重定向到next
中的地址。在echo_url()
中,函数通过request.base_url
属性获得本次访问的路径并返回。
除访问URL数据外,访问其他客户端数据的方式与上面的类似,不再一一演示。下面仅介绍用于访问客户端数据的属性:
from cachelib import SimpleCache # 引入缓存类SimpleCache
from flask import Flask, request, render_template
app = Flask(__name__)
CACHE_TIMEOUT = 300
# 实例化一个缓存类,并设置其timeout属性为300秒
cache = SimpleCache()
cache.timeout = CACHE_TIMEOUT
'''
- 为return_cached()设置装饰器before_request,将其指定为一个在每个请求被处理之前调用的函数。
- return_cached()的内部逻辑为:
如果客户端未提交任何参数,则在缓存中检查该页面是否已经存在,
如果存在,则中断该次请求的调用链,直接将缓存结果返回给客户端。
'''
@app.before_request
def return_cached():
if not request.values:
response = cache.get(request.path)
if response:
print("Got the page from cache.")
return response
print("Will load the page.")
'''
- 为cache_response()设置装饰器after_request,将其指定为一个在每个请求被处理之后调用的函数。
- cache_response()的内部逻辑为:
如果客户端未提交任何参数,则认为该次返回结果具有典型性,将其缓存到对象中以备后续访问。
'''
@app.after_request
def cache_response(response):
if not request.values:
cache.set(request.path, response, CACHE_TIMEOUT)
return response
'''
- 用route装饰器定义URL的具体处理函数index()
'''
@app.route("/get_index")
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run()
'''
整体流程:
客户端在第一次访问页面"/get_index"时,页面内容被缓存在cache对象中;
之后的客户端访问该页面时,Flask直接从cache对象中获取页面内容并直接返回。
'''
Jinja2是Python Web编程中主流的模板语言,从Django模板发展而来,比Django模板的性能更好。
Jinja2模板可以保存在任何基于文本的文件中,比如XML、HTML、CSV等,所以模板文件本身可以接受任何文件后缀,比如.html、.xml等。