Flask使用

前言

所有Web框架的使用都有自己的套路,作为一个框架本质就是一个工具。

安装

pip install Flask

自动安装的依赖

可选安装的依赖

Virtual environments

  • 创建
    $ mkdir myproject
    $ cd myproject
    $ python3 -m venv venv
  • 激活
    $ . venv/bin/activate

简单示例

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
return 'Hello, World!'
  • 运行,设置环境变量 FLASK_APP
    $ FLASK_APP=hello.py flask run
    
  • 监听公共 IP ,确保 debugger 模式已关
    flask run --host=0.0.0.0
    

Debug Mode

export FLASK_ENV=development
or FLASK_APP=hello.py FLASK_ENV=development flask run

Variable Rules

@app.route('/user/<username>')
def show_user_profile(username):
    # show the user profile for that user
    return 'User %s' % 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' % subpath

URL 结尾斜线

  • 不带斜线访问,会 redirects 到带斜线的 URL
    @app.route('/projects/')
    def projects():
        return 'The project page'
    
  • 带斜线访问,会返回404,避免搜索引擎索引同一个页面两次
    @app.route('/about')
    def about():
        return 'The about page'
    

HTTP Methods

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()

URL 构建

from flask import Flask, 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(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

Static Files

在项目下创建 static 目录,存放 static files ,对于 url_for ,static 是保留字,用于生成指向 static 目录下的 URL

url_for('static', filename='style.css')

Rendering Templates

Flask 自动配置Jinja2 template engine. Flask 自动去 templates 目录查找 template

from flask import render_template

@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)

The Request Object

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)
  • 获取 form data, 使用 form 属性
  • 获取 parameters submitted in the URL (?key=value), request.args.get(‘key’, ‘’)

File Uploads

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')
...
  • 使用客户端的文件名存储在服务端,可以使用 secure_filename() 获取文件名

    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))
    

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.
    
  • from flask import make_response

    @app.route(‘/‘)
    def index():

    resp = make_response(render_template(...))
    resp.set_cookie('username', 'the username')
    return resp
    
  • 通常 Flask 自动转换返回值为 response objects ,如果自己想明确的指定 response 需要使用 make_response()

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()
  • 使用 errorhandler() decorator 自定义 error page

    from flask import render_template
    
    @app.errorhandler(404)
    def page_not_found(error):
        return render_template('page_not_found.html'), 404
    

Sessions

  • set a secret key

    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'))
    
  • escape(), 如果不使用模板引擎,escape 可转换 html 特殊字符

  • generate good secret keys, os.urandom(16)

Logging

app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')

部署

  • Flask 自带 wsgi 扩展性能不好,仅适用于开发环境
  • WSGI HTTP Server 可选的有 Gunicorn 和 uWSGI
  • Nginx 作为静态文件访问容器和反向代理、分布式环境下作负载均衡

Gunicorn

  • 安装

    $ pip install gunicorn
    
  • Async Workers
    Gunicorn 默认同步 Workers ,通常我们还需要安装 Eventlet 或则 Gevent ,实现异步 Workers 。greenlet 是默认安装的。eventlet 和 gevent 性能差不多,可以选一个安装。

    $ pip install greenlet            # Required for both
    $ pip install eventlet            # For eventlet workers
    $ pip install gunicorn[eventlet]  # Or, using extra
    $ pip install gevent              # For gevent workers
    $ pip install gunicorn[gevent]    # Or, using extra
    
  • 运行

    gunicorn --workers=2 --worker-class=eventlet hello:app
    
  • 其它参数

    -c CONFIG, --config=CONFIG  Specify a config file in the form $(PATH), file:$(PATH), or python:$(MODULE_NAME).
    -b BIND, --bind=BIND - Specify a server socket to bind. Server sockets can be any of $(HOST), $(HOST):$(PORT), or unix:$(PATH). An IP is a valid $(HOST).
    -n APP_NAME, --name=APP_NAME - If setproctitle is installed you can adjust the name of Gunicorn process as they appear in the process system table (which affects tools like ps and top).
    

布署 Gunicorn