Flask是用python开发的基于Werkzeug和Jinja2的微Web开发框架。
Mako是一个比Jinja2更强大的template
1.Introduction
作为一个微框架,Flask名副其实,5行可以实现Hello World。(基础介绍请参见Flask基本框架文章)
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello, world!"
运行方式
$ pip install Flask
$ Flask_APP=hello.py flask run
* Running on http://localhost:5000/
2.Installation
Werkzeug是WSGI(Web Server Gateway Interface,简单来说用来连接web server和web application)的工具,Jinja2是提供template的工具。
官方推荐virtualenv方式isntall flask。
virtualenv的好处是隔离不同项目间的环境,避免环境冲突。背后的原因是python的package有很多dependency,彼此之间互相影响,而Python本身版本间也有conflicts。
另外,python跨平台特点有时是因为不同平台package异常的问题而失效的,管理好package dependency是pyton的必修课。
Flask支持python2的2.6+,和python3的3.2+。python3修改了unicode和处理byte的方法,影响HTTP data的处理方法,进而影响WSGI的environ data。
使用virtualenv创建虚拟环境的时候,请尽可能注明python版本。
$ virtualenv venv --python=python3.6
启动virtualenv
$ source venv/bin/activate
停止virtualenv
$ deactivate
3.Quickstart
3.1 基本程序解析
from flask import Flask
app = Flask(__name__)
引入Flask class,创建该class的一个instance,参数是应用的module或者package,为Flask指定去哪里找templates, static files等。
@app.route('/')
def hello_world():
return 'Hello, world!'
route装饰器指定哪个URL会触发被修饰的函数。这样的设定可以容易的辨别不同函数对于的响应路由,且可以设置动态路由,如下:
@app.route('/user/')
def show_user(username):
return 'User %s' % username
按照RESTful规范,各级路径名应该是名词性的,动词性的可以用模块来实现动词的功能。
启动文件可以任意命名,比如hello.py,但一般不满意使用flask.py,这样会和Flask起冲突。
flask默认的运行host是127.0.0.1,只能本地debug访问,如果想要他人访问,可以指定host=0.0.0.0
3.2 url_for
url_for()可以产生url,例如:
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/login')
def login(): pass
with app.test_request_context():
print(url_for('login', next = '/'))
输出
/login?next=%2F
3.3 HTTP Methods
默认HTTP methods是GET,可以通过methods参数设定,如下:
from flask import request
@app.route('/login', methods=['GET', 'POST']
def login():
if request.method == 'POST':
do_the_login()
else:
show_the_login_form()
GET: 浏览器要被请求网页的数据;
HEAD: 浏览器要被请求网页的数据,但只关注headers,而不是page content;
POST: 浏览器传递一些新数据,server要储存且只能储存一次数据;
PUT: 和POST很像,区别是PUT时server可以多次储存数据;
DELETE: 删除相应位置的数据;
OPTIONS: 快速告知client一个URL支持什么样的方法。
3.4 Static Files
储存静态资源,用‘static’作为endpoint name产生URL,如:
url_for('static', filename='style.css')
这个文档要被储存在static/style.css
3.5 Rendering Templates
使用python产生HTML非常笨重,因为要保证安全。Flask默认用Jinja2提供template。例如:
from flask import render_template
@app.route('/hello/')
@app.route('/hello/')
def hello(name=None):
reutrn render_template('hello.html', name=name)
使用render_template时,Flask会在templates路径下搜索文件。
在template中,Flask依然支持访问request, session, g, get_flashed_messages()。
3.6 Accessing Request Data
获取client的数据是重要环节,Flask有若干全局变量用来存储这样的数据,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'
return render_template('login.html', error=error)
request还能获取上传的文件,例如:
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允许了解client端的文件名。
操作cookies:
# Reading 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.
# Storing cookies
from flask import make_response
@app.route('/')
def index():
resp = make_response(render_template(...))
resp.set_cookie('username', 'the username')
return resp
3.7 Redirects and Errors
常用两个函数来实现redirets,redirect()和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()
用户登录'/'时被redirect到'/login'再被redirect到错误401页面,无法访问任何资源。
Flask默认给每个error code一个页面,如果要定制,可以用errorhandler()装饰器,如:
from flask import render_template
@app.errorhandler(404)
def page_not_found(error):
return render_template('page_not_found.html'), 404
3.8 Responses
- 如果返回是返回对象类型正确,直接返回;
- 如果返回是一个string,response object会被创建,包含返回的string和默认参数;
- 如果返回一个tuple,可以提供额外信息,比如(response, status, headers)
如果要定制response,使用make_response()方法,例如:
@app.errorhandler(404)
def not_found(error):
resp = make_response(render_template('error.html'), 404)
resp.headers['X-Something'] = 'A value'
return resp
3.9 Sessions
Session是request之外另一个储存用户数据的全局变量,Session通常存储不同request之间的information。Session在cookie之上,且被加密,client只能读取cookie,不能修改。使用Session需要设置secret key。
from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
@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 '''
'''
@app.route('/logout')
def logout():
# remove the username from the session if it's there
session.pop('username', None)
return redirect(url_for('index'))
# set the secret key. keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
上面的代码没有用template,所以使用了escape()。
secret key最好是随机的,可以使用下面方法来处理
import os
os.urandom(24)
3.10 Message Flashing
用户反馈是网站开发非常重要的部分,message flashing是Flask一种非常方便的用户反馈方式。
可以使用flash()和get_flashed_messages()来处理即时消息。
3.11 Logging
log是网站管理的重要组成部分,flask提供下面的方式记录log。
app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')
3.12 Extensions
Flask相当轻便,很多功能可以通过第三方extensions的方式实现,比如Flask-SQLAlchemy提供SQLAlchemy支持。
在使用第三方插件的时候,要尽可能解耦,例如用下面的方式配置。
例如,用第三方sqlalchemy引入db,先创建一个extensions.py来管理第三方引入
# 在extensions里:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
db.slave_session = db.session
再处理app的初始化,比如在core.py进行初始化
# 在core里
from extensions import db
def create_app():
app = Flask(__name__)
app.config.from_object(config)
config.Config.init_app(app)
db.app = app
db.init_app(app)
在model模块从extensions里引入db,这样可以解耦app init和第三方extension
from extensions import db
class BaseModel(db.Model):
...
4. Tutorial
http://flask.pocoo.org/docs/0.12/tutorial/#tutorial
5. User Guide
http://flask.pocoo.org/docs/0.12/