使用 Flask 快速构建应用

在实现一些小需求时,使用 Flask 非常快速,实现 API, 再搭配 Vuejs,很快就能搭建一个 WEB 应用。

准备工作

pip3 install flask
pip3 install mysql-connector

在 https://vuejs.org 下载或者 直接引用 vuejs。

基本使用

Flask 用户指南 | 快速上手

from flask import Flask
import json

app = Flask(__name__)

@app.route("/api")
def hello():
    data = {'name': 'chiyiw', 'age': 25}
    return json.dumps(data, ensure_ascii=False).encode('utf8')

if __name__ == '__main__':
    app.debug = True
    app.run()
    
# 运行方式一:python3 api.py
# 运行方式二:export FLASK_APP=api.py && flask run

路径参数

@app.route("/api/snacks/<id>")
def snack(id):
    # ...

数据库操作

import mysql.connector
import sys

# 获得数据库连接
def getDB():
    user = 'xxx'
    pwd  = 'xxx'
    host = '127.0.0.1'
    db   = 'snacks'
    conn = mysql.connector.connect(user=user, password=pwd, host=host, database=db)
    try:
        conn.ping()
    except:
        print('failed to connect MySQL')
        sys.exit()
    return conn

# select
@app.route("/api/snacks")
def snacks():
	conn = getDB()
    sql = 'select id, name, price from snacks'
    cur = conn.cursor()
    cur.execute(sql)
    data = []
    for (id, name, price) in cur.fetchall():
        data.append({'id': id, 'name': name, 'price': price/100})
    cur.close()
    conn.close()
    return jsonEncode(data) # jsonEncode() 是自定义封装函数

# insert
@app.route("/api/add/<name>/<price>")
def add(name, price, image):
    conn = getDB()
    sql = "insert into snacks(name, price) values('{}',{})".format(name, price)
    cur = conn.cursor()
    cur.execute(sql)
    conn.commit() # commit 操作
    cur.close()
    conn.close()
    return jsonEncode({'code': 0, 'msg': '添加成功'})

参考:Python3连接MySQL数据库 http://smilejay.com/2013/03/python3-mysql-connector/

图片上传

@app.route("/api/upload", methods=['GET','POST'])
def upload():
    if request.method == 'POST':
        f = request.files['file']
        saveDir = '/usr/share/nginx/html/static/snacks/images'
        fileName = '{}.jpg'.format(time.time())
        f.save('{}/{}'.format(saveDir, fileName))
        return jsonEncode({"name": fileName})
    return jsonEncode({})

nginx 配置静态资源

# 配置 Flask 转发
location /snacks/api {
	proxy_pass http://127.0.0.1:5000/api;
}
# 配置静态资源转发
location /snacks/images {
	root /usr/share/nginx/html/static/snacks/images;
	rewrite ^/images/(.*)$ /$1 break;
}

前端实现

此处采用的图片上传正常逻辑是,前端先上传图片,后台接收并返回该图片的 url 等信息,前端将图片显示在预览位置,同时记下 url, 等待输入其他 form 信息后 post ,其中包括了之前返回的图片 url,后台接收并将 form 信息存入数据库。数据库中只存储了图片 url 信息,图片物理上使用 nginx 托管静态资源,或者使用其他对象存储或CDN。

<img class="add-image" :src="this.addImage" alt="">
<input type="file" value="" id="file" @change="uploadImage">

<script>
    uploadImage(e) {
        let formData = new FormData();
        formData.append('file', e.target.files[0]);
        let url = this.apiUrl + "/upload";
        let config = {
            headers: { 'Content-Type': 'multipart/form-data' } // 关键
        };
        axios.post(url, formData, config).then(res => {
            // 上传成功后显示该图片
            this.addImage = this.imgUrl + '/' + res.data.name
        })
    }
</script>

以上的图片上传方案存在一定问题,选择文件后就上传,用户可能重新选择,同时预览加载图片也需要一次请求。可以使用 HTML5 的本地图片预览,只在用户提交时上传图片。

生成环境部署

Flask 自身并不适合直接运行部署,通常依赖其他容器,这里使用流行的 gunicorn, 支持多进程和守护模式。

# pip3 install gunicorn
gunicorn -w 4 -b :5000 --daemon api:app

这种模式没有处理日志,通常日志用于监控和查找问题,gunicorn 日志只有自身日志,并没有整合 Flask 的日志,还需要在 代码中配置:

if __name__ != '__main__':   # 注意此处的 != '__main__'
    # 如果不是直接运行,则将日志输出到 gunicorn 中
    gunicorn_logger = logging.getLogger('gunicorn.error')
    app.logger.handlers = gunicorn_logger.handlers
    app.logger.setLevel(gunicorn_logger.level)

因此,最完整的版本为:

gunicorn -w 4 -b :5000 --log-level=debug --access-logfile run.log --daemon api:app

为了支持重启,可以通过

# pid 输出到 api.pid 文件中
gunicorn api:app -p api.pid --daemon
# 重启时使用
kill -HUP `cat api.pid`

kill -HUP 告知进程进行复位操作并重新加载它们的配置文件。能够在不断开现有连接的情况下,重载进程。

参考: https://spacewander.github.io/explore-flask-zh/13-deployment.html

TOP