Flask框架学习笔记
1. 最简单的flask例子
# 文件名不可取为flask.py,以免发生冲突,本实例以hello.py为代表
from flask import Flask #对象的实例是 WSGI
app = Flask(__name__) # 如果使用单一模块,使用__name__;取决于是作为应用程序的起始还是作为引入的模块,
@app.route('/') # route()是告诉flask哪个url要被触发
def hello_world():
return 'Hello, World!'
2. 如何用命令启动web
# 应该在项目的路径上执行以下方法,而不是在包的路径下
# 方法1:用flask命令启动
$ export FLASK_APP=hello.py
$ flask run
* Running on http://127.0.0.1:5000/
# 方法2:用python -m代替flask命令
$ export FLASK_APP=hello.py
$ python -m flask run
* Running on http://127.0.0.1:5000/
# 若上述方法无效,可能有以下原因:
1. flask版本太旧(<0.11)——升级版本或查看旧版本的启动方式
2. 引入了不合法的名称
# 在window中,set代替export
3. 常用配置
# 在命令行添加以下代码,可使服务公开,可使程序监听所有ip
$ flask run --host=0.0.0.0
# 当代码发生改变时,代码会重新加载启动,且若出现问题将提供一个调试器(注:在window下要用set代替export)
$ export FLASK_ENV=development
$ flask run
# 上述代码可做一下事情:
# 1、触发debugger 2、自动重加载 3、在flask应用上debug
# 使debug与flask环境分离
exporting FLASK_DEBUG=1.
注意:不能在生产环境中使用,有安全风险
4. URL
# 可以在url中增加 <variable_name>变量名,该变量将作为一个关键字参数被方法接收。还可以使用一个转换器来指定参数的类型,如<converter:variable_name>
from markupsafe import escape
@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)
# converter类型:string、int、float、path、uuid
# 唯一的url/重定向行为
# 在下述的两个方法中
# 第一个projects结尾处有斜杠,类似于文件夹。若访问时没有带/,则Flask将重定向到结尾带/的url
# 第二个abiut结尾处没有斜杠,类似于文件。若使用末尾斜杠访问URL会产生404 " Not Found "错误
# 以上有助于保持这些资源的url的唯一性,这有助于搜索引擎避免索引相同的页面两次。
@app.route('/projects/')
def projects():
return 'The project page'
@app.route('/about')
def about():
return 'The about page'
# 构建url
# 使用url_for()方法。它接受函数名作为第一个参数和任意数量的关键字参数,每个参数对应于URL规则的一个变量部分。未知的变量部分作为查询参数附加到URL中,返回对应的url
# 为什么不使用硬编码的方式构建url?
# 原因1、更具有描述性 2、可以一次性改变url,而不是手动改变硬编码 3、更好地处理特殊字符和Unicode数据的转义 4、生成绝对路径,避免相对路径在浏览器中发生意外的行为 5、可以正确处理在根目录之外的应用程序位置
from flask import Flask, url_for
from markupsafe import escape
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
5. HTTP方法
# HTTP方法
# 默认是Get方法,可在route中添加HTTP方法
# 方法对应的Header、Option等会自动添加
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()
6. 静态文件
# 静态文件放置js和css文件
# 使用下列代码,将加载static/style.css文件
url_for('static', filename='style.css')
7. 渲染模板
# 在Python中生成HTML必须自己进行HTML转义,不方便且不安全。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)
# Flask将从templates文件夹查找模板。若原程序是一个module,则templates文件夹位于程序的旁边文件夹;若原程序是一个package,则templates文件夹在package里面。可使用Jinja2模板引擎的全部功能,以下为一个例子:
<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}
# 模板可继承,用于页面内固定元素
# 自动转义是启用的,所以如果name包含HTML,它将自动转义。如果信任一个变量,并且知道它是安全的HTML(例如,因为它来自一个将wiki标记转换为HTML的模块),可通过使用markup类或模板中的|safe过滤器将它标记为安全的。
>>> from markupsafe import Markup
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
Markup(u'<strong>Hello <blink>hacker</blink>!</strong>')
>>> Markup.escape('<blink>hacker</blink>')
Markup(u'<blink>hacker</blink>')
>>> Markup('<em>Marked up</em> » HTML').striptags()
u'Marked up \xbb HTML'
8. 访问请求数据
Flask中提供全局的request对象
# Flask中的全局变量不是常用类型,这些对象实际上是特定上下文本地对象的代理。
# 假设上下文是处理线程。请求传入后,web服务器决定生成一个新线程(或其他东西,底层对象能够处理线程以外的并发系统)。当Flask开始其内部请求处理时,它会发现当前线程是活动的context,并将当前应用程序和WSGI环境绑定到该context(线程)。它以一种智能的方式做到这一点,以便一个应用程序可以调用另一个应用程序而不会中断。因此,可以忽略,除非你在做一些类似单元测试的事情。您将注意到依赖于请求对象的代码将突然中断,因为没有请求对象。解决方案是自己创建一个请求对象并将其绑定到上下文。单元测试最简单的解决方案是使用test_request_context()上下文管理器。与with语句结合使用,它将绑定一个测试请求,以便与之交互。下面是一个例子:
from flask import request
with app.test_request_context('/hello', method='POST'):
# now you can do something with the request until the
# end of the with block, such as basic assertions:
assert request.path == '/hello'
assert request.method == 'POST'
# 另一种方式是将整个WSGI环境传递给request_context()方法
from flask import request
with app.request_context(environ):
assert request.method == 'POST'
9. Request对象
# 访问表单数据(在POST或PUT请求中传输的数据),可以使用form属性。
from flask import 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)
# 获取URL的参数
searchword = request.args.get('key', '')
当key不存在时,会抛出KeyError错误,应该不主动捕捉错误,否则会有400页面。
10. 文件上传
要确保HTML的form中有enctype="multipart/form-data"属性。
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')
...
from flask import request
from werkzeug.utils import secure_filename
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/' + secure_filename(f.filename)) # 查看文件在客户端的文件名,但该名字不能被信任,可先通过secure_filename()方法再传递
...
11. Cookie
# 可以通过cookies属性来访问cookies
# 读取cookies
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.
# 存储cookies
from flask import make_response
@app.route('/')
def index():
resp = make_response(render_template(...))
resp.set_cookie('username', 'the username')
return resp
12. 重定向和错误
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
13. Response
返回值会自动转换成一个response对象。
-
如果返回了正确类型的响应对象,它将直接从视图返回
-
如果是字符串,则创建一个带有该数据和默认参数的响应对象
-
如果是一个dict,则使用jsonify创建一个响应对象
-
如果一个元组被返回,元组中的项可以提供额外的信息。这样的元组必须以
(response, status)
,(response, headers)
或者(response, status, headers)
的形式存在。状态值将覆盖状态代码,并且头可以是附加头值的列表或字典。 -
如果这些都不起作用,Flask将假定返回值是一个有效的WSGI应用程序,并将其转换为一个响应对象。
# 如果想在视图中获取得到的响应对象,可以使用make_response()函数。
def not_found(error):
resp = make_response(render_template('error.html'), 404)
resp.headers['X-Something'] = 'A value'
return resp
14. APIs with JSON
# 返回dict,将转换为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),
}
# 为dict以外的类型创建JSON响应可使用jsonify()函数,它将序列化任何支持的JSON数据类型
@app.route("/users")
def users_api():
users = get_all_users()
return jsonify([user.to_json() for user in users])
15. Session
# sessions在cookies上实现
from flask import Flask, session, redirect, url_for, request
from markupsafe import escape
app = Flask(__name__)
# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/' # 为了使用session,必须有secret_key
@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'))
# 关于基于cookie的会话的注意事项:Flask将获取您放入会话对象中的值,并将它们序列化到一个cookie中。如果您发现一些值在请求之间不持久,那么确实启用了cookie,并且没有得到明确的错误消息,请检查页面响应中的cookie大小,并与web浏览器支持的大小进行比较。
16. 消息闪烁Message Flashing
用这个闪烁系统向用户提供反馈。闪烁系统基本上可以在请求结束时记录一条消息,并在下一个(仅下一个)请求中访问该消息。这通常与布局模板结合使用来公开消息。使用flash()方法来获取消息,你可以使用get_flashhed_messages(),它也可以在模板中找到。
17. Logging日志
app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')