结合Flask官方中文文档,学习Flask基础入门知识
1 Flask介绍
1.1 简介
1.2 流行原因
- 有非常齐全的官方文档,上手非常简单
- 有非常好的扩展机制和第三方扩展环境,工作中常见的软件都会有对应的扩展。自己动手实现扩展也很容易
- 社区活跃度非常高
- 微信框架的形式给开发者带来更大的选择空间
1.3 MVC(Model, View, Controller)设计模式
一种软件设计典范,用一种业务逻辑,使数据,界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面与用户交互的同时,不需要重新编写业务逻辑。
- MVC被独特的发展起来用于映射传统的输入,处理和输出功能在一个逻辑的图形化界面结构中。
- 核心思想:解耦
- 优点:降低各模块之间的耦合性,方便变更,更容易重构代码,最大程度实现了代码的重用。
- Model:用于封装与应用程序的业务逻辑相关的数据及对数据的处理方法,是Web应用程序中用于处理应用程序的数据逻辑部分,Model通常只提供功能性的接口,通过这些接口可以获取Model的所有功能。
- View:负责数据的显示和呈现,View是对用户 的直接输出。
- Controller:负贵从用户端收集用户的输入,可以看成提供View的反向功能,主要处理用户交互。
2 安装(Linux)
2.1 创建一个虚拟环境
创建一个项目文件夹,然后创建一个虚拟环境。创建完成项目文件夹中会有一个venv
文件夹:
1
2
3
|
mkdir myproject
cd myproject
python3 -m venv venv
|
2.2 激活虚拟环境
在开始工作前,先要激活响应的虚拟环境:
2.3 安装Flask
在已激活的虚拟环境中可以使用如下命令安装Flask:
2.4 安装virtualenv
在Linux下,virtualenv 通过操作系统的包管理安装:
1
2
|
# Debian,Ubuntu
sudo apt-get install python-virtualenv
|
3 Hello World
1
2
3
4
5
6
7
8
9
|
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello,World!'
if __name__ == '__main__':
app.run()
|
笔记
- 首先导入了
Flask
类。 该类的实例将会成为WSGI应用。
- 接着创建一个该类的实例。第一个参数是应用模块或者包的名称。如果使用一个单一模块(就像本例),
那么应当使用
name
,因为名称会根据这个模块是按应用方式使用还是作为一个模块导入而发生变化(可能
是 ‘main’ ,也可能是实际导入的名称)。这个参数是必需的,这样 Flask 才能知道在哪里可以找到模板和
静态文件等东西。
- 然后使用
route()
装饰器来告诉 Flask 触发函数的 URL 。
- 函数名称被用于生成相关联的URL。函数最后返回需要在用户浏览器中显示的信息。
把它保存为
hello. py
或其他类似名称。不要使用flask.py
作为应用名称,这会与 Flask 本身发生冲突。
4 参数配置
4.1 利用app.run()配置
- 在启动的时候还可以添加参数,在
run()
中
- debug 是否开启调试模式,开启后修改过python代码会自动重启
- threaded 是否开启多线程
- port 启动指定服务器的端口号
- host 主机,默认是127.0.0.1,指定为0.0.0.0代表本机所有ip
1
2
|
if __name__ == '__main__':
app.run(debug=True,port=8000,host='0.0.0.0')
|
4.2 使用config.py
配置文件
1
2
|
# 以DEBUG为例
DEBUG = True
|
1
|
app.config.from_object('config')
|
1
|
app.run(host='0.0.0.0', debug=app.config['DEBUG'], port=90)
|
重要
app.config.from_object('config')
载入配置文件,使用时DEBUG
必须大写
- config 配置文件中的参数必须全部大写
DEBUG
的值默认值为 False
4.3 使用flask-script插件配置
4.3.1 安装使用
1
|
pip3 install flask-script
|
1
|
manager = Manager(app=app)
|
4.3.2 参数使用
1
2
3
4
5
6
7
8
|
-d #是否开启调试模式
-r #是否自动重新加载文件
-h, -host #指定主机
-p, -port #指定端口
-threaded #是否使用多线程
-?, -help #查看帮助
#示例:
python3 manage.py runserver --help
|
5 请求流程
6 路由
路由:将从客户端发送过来的请求分发到指定函数上
1
2
3
4
5
6
7
|
@app.route('/')
def index():
return 'Index Page'
@app.route('/hello')
def hello():
return 'Hello,World'
|
6.1 变量规则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@app.route('/user/<username>')
def show_user_profile(username):
# show the user profile for that user
return 'User %s' % escape(username)
@app.route('/post/<int:post_id>')
def show_post(post_id):
# show the post with the given id, the id is an integer
return 'Post %d' % post_id
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
# show the subpath after /path/
return 'Subpath %s' % escape(subpath)
|
笔记
参数
- 路径参数
- 请求参数
- get 参数在路径中?之后
- post参数在请求体中
Flask中参数
- 都是关键字参数
- 默认标识是
<>
- name需要和对应的视图函数的参数名字保持一致
- 参数允许有默认值
- 如果有默认值,那么在路由中,不传输参数也可以
- 如果没有默认值,参数在路由中必须传递
- 默认参数类型是字符串,写法
<converter:variable_name>
- converter类型
- string 默认值,会将
/
认为是参数分割符
- int 接收整型
- float 接收浮点型
- path 接收到的数据格式是字符串,会将斜线
/
认为是一个字符
- uuid 只接收uuid字符串,唯一码,一种生成规则
- any 列出的元组中的任意一个
6.2 唯一的URL / 重定向行为
以下的两条规则的不同之处在于是否使用尾部的/
:
1
2
3
4
5
6
7
|
@app.route('/projects/')
def projects():
return 'The project page'
@app.route('/about')
def about():
return 'The about page'
|
6.3 反向解析
url_for('函数名',参数名=value)
笔记
为什么不在把 URL 写死在模板中,而要使用反转函数 url_for()
动态构建?
- 反转通常比硬编码 URL 的描述性更好。
- 可以只在一个地方改变 URL ,而不用到处乱找。
- URL 创建会为你处理特殊字符的转义和 Unicode 数据,比较直观。
- 生产的路径总是绝对路径,可以避免相对路径产生副作用。
- 如果应用是放在 URL 根路径之外的地方(如在
/myapplication
中,不在 /
中), url_for()
会妥善处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
from flask import Flask, escape, url_for
app = Flask(__name__)
@app.route('/')
def index():
return 'index'
@app.route('/login')
def login():
return 'login'
@app.route('/user/<username>')
def profile(username):
return '{}\'s profile'.format(escape(username))
with app.test_request_context():
print(url_for('index'))
print(url_for('login'))
print(url_for('login', next='/'))
print(url_for('profile', username='John Doe'))
#运行结果
/
/login
/login?next=/
/user/John%20Doe
|
6.4 HTTP 方法
Web 应用使用不同的 HTTP 方法处理 URL 。当你使用 Flask 时,应当熟悉 HTTP 方法。 缺省情况下,一个路由只回应 GET
请求。 可以使用 route()
装饰器的 methods
参数来处理不同的 HTTP 方法:
1
2
3
4
5
6
7
8
9
|
from flask import request
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
return do_the_login()
else:
return show_the_login_form()
|
methods中指定请求方法
7 静态文件
动态的 web 应用也需要静态文件,一般是 CSS 和 JavaScript 理想情况下服务器已经配置好了提供静态文件的服务。但是在开发过程中, Flask 也能做好这项工作。只要在包或模块旁边创建一个名为 static
的文件夹就行了。 静态文件位于应用的 /static
中。
使用特定的 ‘static’ 端点就可以生成相应的 URL
1
2
|
url_for('static', filename='style.css')
# 这个静态文件在文件系统中的位置应该是 static/style.css
|
8 模板
模板是呈现给用户的界面,在MVT中充当T的角色,实现了VT的解耦,开发中VT有着N:M的关系,一个V可以通用任意T,一个T可以被任意V调用。
Flask 会在 templates
文件夹内寻找模板。因此,如果应用是一个模块, 那么模板文件夹应该在模块旁边;如果是一个包,那么就应该在包里面:
1
2
3
|
/application.py
/templates
/hello.html
|
1
2
3
4
|
/application
/__init__.py
/templates
/hello.html
|
8.1 Jinja2
Flask中使用Jinja2模板引擎
Jinja2由Flask作者开发
-
一个现代化设计和友好的Python模板语言
-
模仿Django的模板引擎
-
优点:
- 速度快,被广泛使用
- HTML设计和后端Python分离
- 较少Python复杂度
- 非常灵活,快速和安全
- 提供了控制,继承等高级功能
-
Flask 自动配置 Jinja2 模板引擎。
使用 render_template()
方法可以渲染模板,只要提供模板名称和需要作为参数传递给模板的变量就行了。下面是一个简单的模板渲染例子:
1
2
3
4
5
6
|
from flask import render_template
@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', name=name)
|
8.2 模板语法
模板语法主要分为两种:变量、标签
8.2.1 变量
模板中的变量{{ var }}
- 视图传递给模板的数据
- 前面定义出来的数据
- 变量不存在,默认忽略
8.2.2 标签
模板中的标签{% tag %}
8.3 结构标签
8.3.1 block
1
2
|
{% block xxx %}
{% endblock %}
|
- 块操作
- 父模板挖坑,子模板填坑
- 首次出现挖坑,非首次填坑
- 多次填坑会出现覆盖,不想覆盖使用{{super()}}
8.3.2 extends
1
2
3
|
{% extends 'xxx' %}
<!--继承后保留块中的内容-->
{{super()}}
|
8.3.3 include
- 包含,将其他html包含进来,体现的是由零到一的概念
- 能用block + extends实现的,尽量不要使用include
8.3.4 marco
1
2
3
|
{% marco hello(name) %}
{% endmarco %}
{{name}}
|
- 宏定义,可以在html中定义函数
- 可以接收参数
- 通过调用函数生成html
- 支持导入操作
1
|
{% from xxx import yy %}
|
8.4 for 循环
1
2
3
4
5
|
{% for item in cols %}
AA
{% else %}
BB
{% endfor %}
|
- 可以使用和Python一样的
for...else
- 也可以获取循环信息loop
- loop.first
- loop.last
- loop.index
- loop.index()
- loop.revindex
- loop.revindex()
8.5 过滤器
|
|
capitalize |
驼峰命名 |
lower |
变为小写 |
upper |
变为大写 |
title |
标题 |
trim |
去掉空格 |
reverse |
反转 |
format |
格式化 |
striptags |
渲染之前,将值中标签去掉 |
8.6 Flask-Bootstrap
官方文档:Flask-Bootstrap
8.6.1 安装
1
|
pip3 install flask-bootstrap
|
8.6.2 导入和加载扩展
1
2
3
4
5
6
7
8
9
10
|
from flask import Flask
from flask_bootstrap import Bootstrap
def create_app():
app = Flask(__name__)
Bootstrap(app)
return app
# do something with app...
|
8.6.2 创建基于Bootstrap的模板
1
2
3
4
5
6
7
8
9
10
11
12
|
{% extends "bootstrap/base.html" %}
{% block title %}This is an example page{% endblock %}
{% block navbar %}
<div class="navbar navbar-fixed-top">
<!-- ... -->
</div>
{% endblock %}
{% block content %}
<h1>Hello, Bootstrap</h1>
{% endblock %}
|
9 Request
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
from flask import request
@app.route('/request', methods=['GET', 'POST'])
def req():
print(request)
print(request.method)
print(request.data)
# arguments 参数,get请求参数
print(request.args)
print(request.args.get('name'))
# post 相关请求都会有数据
print(request.form)
print(request.files)
print(requset.cookies)
#ip地址
print(requset.remote_addr)
# 浏览器标识
print(requset.user_agent)
print(requset.user_url)
|
10 Response
服务器返回给客户端的数据
由程序员创建,返回Response对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@app.route('/response')
def(resp):
result = render_template('hello.html')
print(result)
print(type(result))
response = make_response('<h3>响应</h3>',400)
print(response)
print(type(response))
return response
|
11 重定向和错误
- redirect()
- url_for(‘函数名’,参数=value)
- 使用
redirect()
函数可以重定向。使用 abort()
可以更早退出请求,并返回错误代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
from flask import abort, redirect, url_for
@app.route('/')
def index():
return redirect(url_for('login'))
@app.route('/login')
def login():
abort(401)
this_is_never_executed()
# 上例实际上是没有意义的,它让一个用户从索引页重定向到一个无法访问的页面(401 表示禁止访问)。
# 但是上例可以说明重定向和出错跳出是如何工作的。
|
- 缺省情况下每种出错代码都会对应显示一个黑白的出错页面。使用 errorhandler() 装饰器可以定制出错页面:
1
2
3
4
5
6
|
from flask import render_template
@app.errorhandler(404)
def page_not_found(error):
return render_template('page_not_found.html'), 404
#注意 render_template() 后面的 404 ,这表示页面对应的出错 代码是 404 ,即页面不存在。缺省情况下 200 表示:一切正常。
|
12 JSON
JSON 格式的响应是常见的,用 Flask 写这样的 API 是很容易上手的。如果从视图返回一个 dict
,那么它会被转换为一个 JSON 响应。
1
2
3
4
5
6
7
8
|
@app.route("/me")
def me_api():
user = get_current_user()
return {
"username": user.username,
"theme": user.theme,
"image": url_for("user_image", filename=user.image),
}
|
如果 dict
还不能满足需求,还需要创建其他类型的 JSON 格式响应,可以使用 jsonify()
函数。该函数会序列化任何支持的 JSON 数据类型。
1
2
3
4
|
@app.route("/users")
def users_api():
users = get_all_users()
return jsonify([user.to_json() for user in users])
|
13 蓝图
- Blueprint 是一种组织一组相关视图及其他代码的方式。与把视图及其他 代码直接注册到应用的方式不同,蓝图方式是把它们注册到蓝图,然后在工厂函数中把蓝图注册到应用。
- flask中用来解决上帝文件问题,将请求从主文件中拆分到多个文件中。
13.1 插件安装
1
|
pip3 install flask-blueprint
|
13.2 初始化蓝图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
# manager.py
from flask import Flask
from flask_script import Manager
from App.views import bp
app = Flask(__name__)
# 在app中注册
app.register_blueprint(blueprint=bp)
manager = Manager(app=app)
if __name__ == '__main__'
manager.run()
# /App/views
from flask import Blueprint
# 创建一个类,构造一个蓝图
bp = Blueprint('bp',__name__)
# 使用
@bp.route('/')
def.hello():
return 'Hello Blueprint!'
|
14 Cookie
-
客户端会话技术
-
数据都是存储在浏览器中
-
支持过期
-
不能跨域名
-
不能跨浏览器
-
cookie是通过Response来进行操作
-
flask中的cookie可以直接支持中文
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 读取cookie
from flask import request
@app.route('/')
def index():
username = request.cookies.get('username')
# use cookies.get(key) instead of cookies[key] to not get a
# KeyError if the cookie is missing.
# 存储cookie
from flask import make_response
@app.route('/')
def index():
resp = make_response(render_template(...))
resp.set_cookie('username', 'the username')
return resp
|
15 Session
-
服务端会话技术
-
对数据进行数据安全操作
-
默认在内存中
-
Flask-Session 默认有效期31天
15.1 Session使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
# 使用会话之前必须设置一个密钥。
# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
@app.route('/')
def index():
if 'username' in session:
return 'Logged in as %s' % escape(session['username'])
return 'You are not logged in'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
session['username'] = request.form['username']
return redirect(url_for('index'))
return '''
<form method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
'''
@app.route('/logout')
def logout():
# remove the username from the session if it's there
session.pop('username', None)
return redirect(url_for('index'))
|
15.2 Flask-Session插件
官方文档:Flask-Session
1
|
pip3 install Flask-Session
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
from flask import Flask, session
from flask.ext.session import Session
app = Flask(__name__)
# Check Configuration section for more details
app.config['SESSION_TYPE'] = 'redis'
Session(app=app)
@app.route('/set/')
def set():
session['key'] = 'value'
return 'ok'
@app.route('/get/')
def get():
return session.get('key', 'not set')
|
16 消息闪现
- 闪现系统的基本工作原理是在请求结束时记录一个消息,提供且只提供给下一个请求使用。通常通过一个布局模板来展现闪现的消息。
flash()
用于闪现一个消息。在模板中,使用 get_flashed_messages() 来操作消息。
17 登录示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
from flask import Flask, flash,session, redirect, render_template, url_for, escape, request
app = Flask(__name__)
# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
@app.route('/')
def index():
if 'username' in session:
return 'Logged in as %s' % escape(session['username'])
return 'You are not logged in'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
if request.form['username'] != 'admin' or \
request.form['password'] != 'secret':
flash(u'Invalid password provided', 'error')
else:
flash('You were successfully logged in')
return redirect(url_for('index'))
return render_template('login.html', error=error)
@app.route('/logout')
def logout():
# remove the username from the session if it's there
session.pop('username', None)
return redirect(url_for('index'))
|
以下是实现闪现的 layout.html
模板:
1
2
3
4
5
6
7
8
9
10
11
12
|
<!doctype html>
<title>My Application</title>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for category, message in messages %}
<li> class="{{ category }}">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{% block body %}{% endblock %}
|
以下是继承自 layout.html
的 index.html
模板:
1
2
3
4
|
{% block body %}
<h1>Overview</h1>
<p>Do you want to <a href="{{ url_for('login') }}">log in?</a>
{% endblock %}
|
以下是同样继承自 layout.html
的 login.html
模板:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
{% extends "layout.html" %}
{% block body %}
<h1>Login</h1>
{% if error %}
<p class=error><strong>Error:</strong> {{ error }}
{% endif %}
<form method=post>
<dl>
<dt>Username:
<dd><input type=text name=username value="{{
request.form.username }}">
<dt>Password:
<dd><input type=password name=password>
</dl>
<p><input type=submit value=Login>
</form>
{% endblock %}
|