功能函数
after_this_request
临时回调
from flask import request, after_this_request
@app.before_request
def detect_user_language():
language = request.cookies.get('user_lang')
if language is None:
language = guess_language_from_request()
@after_this_request
def remember_language(response):
response.set_cookie('user_lang', language)
return response
g.language = language
流处理
基础用法 - 基于生成器构建响应
from flask import Response
@app.route('large.csv')
def generate_large_csv():
def generate():
for row in iter_all_rows():
yield ','.join(row) + '\n'
return Response(generate(), mimetype='text/csv')
模板中使用
from flask import Response
def stream_template(template_name, **context):
app.update_template_context(context)
t = app.jinja_env.get_template(template_name)
rv = t.stream(context)
rv.enable_buffering(5)
return rv
@app.route('/my-large-page.html')
def render_large_template():
rows = iter_all_rows()
return Response(stream_template('the_template.html', rows=rows))
情境中使用
from flask import stream_with_context, request, Response
@app.route('/stream')
def streamed_response():
def generate():
yield 'Hello '
yield request.args['name']
yield '!'
return Response(stream_with_context(generate()))
添加页面图标
import os
from flask import send_from_directory
@app.route('/favicon.ico')
def favicon():
return send_from_directory(os.path.join(app.root_path, 'static'),
'favicon.ico', mimetype='image/vnd.microsoft.icon')
连接MongoDB
构建连接
from flask import Flask
from flask_mongoengine import MongoEngine
app = Flask(__name__)
app.config['MONGO_SETTINGS'] = {
'db': 'myapp',
}
db = MongoEngine(app)
映射文档
import mongoengine as me
class Movie(me.Document):
title = me.StringField(required=True)
year = me.IntField()
rated = me.StringField()
director = me.StringField()
actors = me.ListField()
class Imdb(me.EmbeddedDocument):
imdb_id = me.StringField()
rating = me.DecimalField()
votes = me.IntField()
class Movie(me.Document):
...
imdb = me.EmbeddedDocumentField(Imdb)
创建数据
bttf = Movie(title='Back To The Future', year=1985)
bttf.actors = [
'Michael J. Fox',
'Christopher Lloyd'
]
bttf.imdb = Imdb(imdb_id='tt0088763', rate=8.5)
bttf.save()
查询数据
bttf = Movies.objects(title='Back To The Future').get_or_404()
some_theron_movie = Movie.objects(actors_in=['Charlize Theron']).first()
for recents in Movie.objects(year__gte=2017):
print(recents.title)
惰性加载
- STEP1: 分离视图文件
views.py
def index():
pass
def user(username):
pass
app.py
from flask import Flask
import views
app = Flask(__name__)
app.add_url_rule('/', view_func=views.index)
app.add_url_rule('/user/<username>', view_func=views.user)
- STEP2: 添加延迟加载
lazyview.py
from werkzeug.utils import import_string, cached_property
class LazyView(object):
def __init__(self, import_name):
self.__module__, self.__name__ = import_name.rsplit('.', 1)
self.import_name = import_name
@cached_property
def view(self):
return import_string(self.import_name)
def __call__(self, *args, **kwargs):
return self.view(*args, **kwargs)
app.py
from flask import Flask
import views
app = Flask(__name__)
app.add_url_rule('/', view_func=LazyView('views.index'))
app.add_url_rule('/user/<username>', view_func=LazyView('views.user'))
- STEP3: 代码优化
url.py
def url(import_name, url_rules=[], **options):
view = LazyView(import_name)
for url_rule in url_rules:
app.add_url_rule(url_rule, view_func=view, **options)
url('views.index', ['/'])
url_rules = ['/user/', '/user/<username>']
url('views.user', url_rules)
自定义错误页面
常见的错误类型
- 404 Not Found: 页面或者资源不存在
- 403 Forbidden: 客户端未得到授权
- 410 Gone: 页面或者资源已经删除
- 500 Internal Server Error: 程序出错或者服务端过载
定义一个错误处理器
from flask import render_template
# 装饰器模式
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
# 工厂模式
def page_not_found(e):
return render_template('404.html'), 404
def create_app(config_filename):
app = Flask(__name__)
app.register_error_handler(404, page_not_found)
return app
# 返回json数据
from flask import abort, jsonify
@app.errorhandler(404)
def resource_not_found(e):
return jsonify(error=str(e)), 404
@app.route('/cheese')
def get_one_cheese():
resource = get_resource()
if resource is None:
abort(404, description='Resource not found')
return jsonify(resource)
消息闪现
app.py
from flask import Flask, flash, redirect, render_template, \
request, url_for
app = Flask(__name__)
app.scret_key = b'_5#y2L"F4Q8z\n\xec]/'
@app.route('/')
def index():
return render_template('index.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
request.form['username'] != 'admin' or \
request.form['password'] != 'secret':
error = 'Invalid credentials'
else:
flash('You were successfully logged in')
return redirect(url_for('index'))
return render_template('login.html', error=error)
layout.html
<!DOCTYPE html>
<title>My Application</title>
{% with message = get_flasked_message() %}
{% if message %}
<ul class=flases>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
定义闪现消息类型
flash(u'Invalid password provided', 'error')
{% with message = get_flashed_messages(with_categories=true) %}
{% if message %}
<ul class=flashes>
{% for category, message in messages %}
<li class="{{ category }}">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
过滤闪现消息
{% with errors = get_flashed_message(category_filter=["error"]) %}
{% if errors %}
<div class="alert-message block-message error">
<a class="close" href="#">
<ul>
{%- for msg in errors %}
<li>{{ msg }}</li>
{% endfor -%}
</ul>
</a>
</div>
{% endif %}
{% endwith %}
缓存
使用Flask-Caching
扩展实现缓存。
from flask import Flask
from flask_caching import Cache
config = {
"DEBUG": True,
"CACHE_TYPE": "SimpleCache",
"CACHE_DEFAULT_TIMEOUT": 300
}
app = Flask(__name__)
app.config.from_maping(config)
cache = Cache(app, config=config)
cache.init_app(app)
@app.route("/")
@cache.cached(timeout=50, key_prefix='all_comments')
def index():
return render_template('index.html')
@cache.cached(timeout=50, key_prefix='all_comments')
def get_all_comments():
comments = do_serious_dbio()
return [x.author for x in comments]
cached_comments = get_all_comments()
class Person(db.Model):
@cache.memoize(50)
def has_membership(self, role_id):
return Group.query.filter_by(user=self, role_id=role_id).count() >= 1
def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, self.id)
class Foobar(object):
@classmethod
@cache.memoize(5)
def big_foo(cla, a, b):
return a + b + random.randrange(0, 100000)
cache.delete_memoized(Foobar.big_foo, Foobar, 5, 2)
class RedisCache(BaseCache):
def __init__(self, servers, default_timeout=500):
pass
@classmethod
def factory(cls, app, args, kwargs):
args.append(app.config['REDIS_SERVERS'])
return cls(*args, **kwargs)
上传文件
import os
from flask import Flask, flash, request, redirect, url_for, send_from_directory
from wekzeug.utils import secure_filename
UPLOAD_FOLDER = '<path>'
ALLOW_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('upload_file', filename=filename)
return '''
<!doctype html>
<title>Uplaod new File</title>
<h1>Upload new File</h1>
<form method=post enctype=multipart/form-data>
<input type=file name=file>
<input type=submit value=Upload>
</form>
'''
@app.route('/uploads/<filename>')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
URL处理器
from flask import Flask, g
app = Flask(__name__)
@app.route('/<lang_code>/')
def index(lang_code):
g.lang_code = lang_code
...
@app.route('/<lang_code>/about')
def about(lang_code):
g.lang_code = lang_code
@app.url_defaults
def add_language_code(endpoint, values):
if 'lang_code' in values or not g.lang_code:
return
if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
values['lang_code'] = g.lang_code
@app.url_value_preprocessor
def pull_lang_code(endpoint, values):
g.lang_code = values.pop('lang_code', None)
API异常处理
from flask import jsonify
class InvalidUsage(Exception):
status_code = 404
def __init__(self, message, status_code=None, payload=None):
Exception.__init__(self)
if status_code is not None:
self.status_code = status_code
self.payload = payload
def to_dict(self):
rv = dict(self.payload or ())
rv['message'] = self.message
return rv
@app.errorhandler(InvalidUsage)
def handle_invalid_useage(error):
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response
@app.route('/foo')
def get_foo():
raise InvalidUsage('This view is gone', status_code=410)
应用工厂
def create_app(config_filename):
app = Flask(__name__)
app.config.from_pyfile(config_filename)
from .model import db
db.init_app(app)
from .views.admin import admin
from .views.frontend import frontend
app.register_blueprint(admin)
app.register_blueprint(frontend)
return app
Flask继承
from flask import Flask, Request
from werkzeug.datastructures import ImmutableOrderedMultiDict
class MyRequest(Request):
parameter_storage_class = ImmutableOrderedMultiDict
class MyFlask(Flask):
request_class = MyRequest
单页面应用
from flask import Flask, jsonify
app = Flask(__name__, static_folder='app', static_url_path='/app')
@app.route('/heartbeat')
def heartbeat():
return jsonify({'status': 'healthy'})
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
return app.send_static_file('index.html')