Flask学习记录
记录服务框架的学习
参考官方教程 Flask框架学习
Flask是什么?
Flask是一个基于Python的微型web框架,它能够快速搭建一个网页或web服务。
目录结构
Flask的目录结构大致如下:
flask-demo/
├ run.py # 应用启动程序
├ config.py # 环境配置
├ requirements.txt # 列出应用程序依赖的所有Python包
├ tests/ # 测试代码包
│ ├ __init__.py
│ └ test_*.py # 测试用例
└ myapp/
├ admin/ # 蓝图目录
├ static/
│ ├ css/ # css文件目录
│ ├ img/ # 图片文件目录
│ └ js/ # js文件目录
├ templates/ # 模板文件目录
├ __init__.py
├ forms.py # 存放所有表单,如果多,将其变为一个包
├ models.py # 存放所有数据模型,如果多,将其变为一个包
└ views.py # 存放所有视图函数,如果多,将其变为一个包
快速上手
安装直接pip安装就行,就省略了
HelloWorld
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
if __name__ == '__main__':
app.debug = True # 设置调试模式,生产模式的时候要关掉debug
app.run()
python运行后访问localhost:5000即可看到
- Flask类是主要的框架,__name__使得Flask能够找到template等其他东西,让flask.helpers.get_root_path函数通过传入这个名字确定程序的根目录,以便获得静态文件和模板文件的目录。
- 在flask中,定义路由最简便的方式,是使用程序实例的app.route装饰器,把装饰的函数注册为路由
绑定参数
除了字符串这种静态路由,route也可以绑定参数的路由通过"
from markupsafe import escape
@app.route('/user/<username>')
def show_user_profile(username):
# show the user profile for that user
return f'User {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 f'Post {post_id}'
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
# show the subpath after /path/
return f'Subpath {escape(subpath)}'
- 这里的path类指带斜杠的字符串
url_for
url_for可以通过反向路径构造指定函数的路由
from flask import url_for
@app.route('/')
def index():
return 'index'
@app.route('/login')
def login():
return 'login'
@app.route('/user/<username>')
def profile(username):
return f'{username}\'s profile'
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'))
'''
Outputs:
/
/login
/login?next=/
/user/John%20Doe
'''
静态文件
静态文件在flask中存储在static文件夹下,使用url_for()能够构造静态文件的URL
url_for('static', filename='style.css')
HTTP方法
HTTP常用的请求方法
- get 用来获取资源
- post 传输实体获得回复
- put 传输文件
- head 获取首部
- delete等等
在默认情况下,一个路由只会回应GET请求,但是可以使用route()装饰器中的method参数来处理不同的HTTP方法。
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()
如果GET被设定为可接受,则flask自动的会支持head方法
加载模板
Flask配置了Jinja2模板引擎,我们可以使用它来绘制界面。 通过render_template():
from flask import render_template
@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', name=name)
这个hello.html存储在当前目录中的templates文件夹 Jinja2的模板提供了许多强大功能,例如:
<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}
在模板中同样可以使用各种各样的函数并访问变量。
模板继承
模板继承对于需要频繁重用的网页部件例如导航栏等十分有用。 我们可以创建一个父类模板layout.html:
<!doctype html>
<title>Hello Sample</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
<div class="page">
{% block body %}
{% endblock %}
</div>
然后再创建一个子类,并继承layout.html:
{% extends "layout.html" %}
{% block body %}
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello World!</h1>
{% endif %}
{% endblock %}
此时子类的内容会被加载到block body中,父类的其他内容依然会被显示。
HTML自动转义
我们有时不希望提交的表单中含有的HTML标签被自动转移,因此可以使用Markup类:
from flask import Flask, Markup
app = Flask(__name__)
@app.route('/')
def index():
return Markup('<div>Hello %s</div>') % '<em>Flask</em>'
format应当放在Markup外面
Markup还有许多方法
request
request对象用来处理请求
@app.route('/login', methods=['POST', 'GET'])
def login():
error = None
if request.method == 'POST':
if valid_login(request.form['username'],
request.form['password']):
return log_the_user_in(request.form['username'])
else:
error = 'Invalid username/password'
# the code below is executed if the request method
# was GET or the credentials were invalid
return render_template('login.html', error=error)
常用方法
- request.method可以用来获取当前的HTTP方法
- request.form可以得到提交表单的内容,如果form中没有需要的键值就会报KeyError,并出现400错误页面
- request.args.get可以获取URL中的参数
文件上传
传输文件需要在HTML提交表单中设置enctype="multipart/form-data"
上传的文件可以通过request.file获得,是一个python的file对象,并拥有一个save()方法用来存储到本地。
from flask import request
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/uploaded_file.txt')
...
Cookies
可以使用request.cookies访问cookies,使用set_cookie()设置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.
from flask import make_response
@app.route('/')
def index():
resp = make_response(render_template(...))
resp.set_cookie('username', 'the username')
return resp
make_response()可以生成一个response对象用来对网页返回的内容进行更多修改(例如cookies)
每个经过app装饰器修饰的函数会将return的内容自动转换为response对象。
重导向与报错
redirect()可以改变访问的路由,abort(int)可以进行报错,实际开发中并不经常使用abort,而是进行异常的抛出或捕获。
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()
使用errorhandler()装饰器来装饰报错页面
from flask import render_template
@app.errorhandler(404)
def page_not_found(error):
return render_template('page_not_found.html'), 404
此外,errorhandler也可以自定义异常类型
API
常用的API接口调用会返回JSON,使用Flask返回Json十分简单,返回字典格式即可
@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),
}
对于返回不是字典的api,也可以通过jsonify()来序列化数据
from flask import jsonify
@app.route("/users")
def users_api():
users = get_all_users()
return jsonify([user.to_json() for user in users])
Session
session会话,和request一样是flask中的全局对象,用来保存当前请求的一些状态,用来再之后与同一用户的通信中共享信息。session将信息加密并保存在cookies中。
from flask import session
# 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 f'Logged in as {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'))
日志
app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')
SocketIO
WebSocket - 基于 Python 的主流实现方式总结 websocket可以用来进行服务器与客户端之间的持续双工通信 flask作为服务端可以使用flask-socketio库 注意安装需要同时安装python-socketio、python-engineio等等,且版本需要匹配
异步任务
有时需要处理一些耗时的任务,那么就可以通过多线程、协程等异步的进行。 Python Flask后端异步处理
python自带的多线程
我们可以使用ThreadPoolExecutor进行
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(4)
@app.route('/')
def someTask():
executor.submit(longTimeTask, *args)
Celery
python自带的库虽然很方便,但是有时我们需要随时获取任务的各种结果,对任务进行更加复杂的操作。 我们还可以使用Celery进行异步分布式任务队列服务,它支持定时任务、即时任务等等。
将localhost映射到公网
通过使用 ngrok ,我们可以把本地的端口映射到公网,在别的地方都能访问,方便我们进行测试 打开下载的exe,输入
> ngrok http 5000
就可以将端口5000映射到公网,具体的网址会显示在cmd中。
测试工具
API
待补充……