Python使用Web框架Flask开发项目

  • Post category:Python

下面我会详细地讲解如何使用Python的Web框架Flask进行项目开发。

安装Flask

首先,在开始使用Flask之前,需要先安装Flask。可以使用pip安装Flask,方法如下:

pip install Flask

创建Flask应用

接下来,我们需要创建一个Flask应用,可以通过下面的代码创建一个最简单的Flask应用:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello, Flask!'

这段代码创建了一个Flask应用,并在根路由下定义了一个简单的视图函数index(),用于返回一个字符串。

运行Flask应用

Flask应用创建完成后,需要运行应用才能在浏览器中看到效果。运行Flask应用的代码如下:

if __name__ == '__main__':
    app.run()

在运行Flask应用之前,需要在命令行中导出FLASK_APP环境变量。假设应用代码保存在app.py文件中,那么运行命令如下:

export FLASK_APP=app.py

然后就可以在命令行中运行Flask应用:

flask run

Flask应用的默认端口是5000,可以通过访问http://localhost:5000/来查看效果。

动态路由

除了根路由之外,Flask应用还可以定义其他路由,如下所示:

@app.route('/user/<username>')
def show_user_profile(username):
    return 'User %s' % username

@app.route('/post/<int:post_id>')
def show_post(post_id):
    return 'Post %d' % post_id

这里定义了两个动态路由,可以匹配类似/user/john和/post/123这样的URL。其中,是动态参数,可以在视图函数中使用。

使用模板

Flask应用通常需要渲染HTML模板,Flask默认支持Jinja2模板引擎。可以使用Jinja2模板引擎渲染模板,如下所示:

from flask import render_template

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

例如,在templates文件夹下定义一个hello.html文件,包含如下代码:

{% if name %}
    <h1>Hello, {{ name }}!</h1>
{% else %}
    <h1>Hello, World!</h1>
{% endif %}

然后,浏览器访问http://localhost:5000/hello/john,即可以看到页面上输出Hello, john!。

使用数据库

除了渲染模板,Flask应用还可以与数据库交互。Flask支持多种数据库,如SQLite、MySQL、PostgreSQL等。以下是使用SQLite数据库的示例代码:

import sqlite3
from flask import g

DATABASE = '/tmp/flaskr.db'

def get_db():
    if not hasattr(g, 'sqlite_db'):
        g.sqlite_db = sqlite3.connect(DATABASE)
        g.sqlite_db.row_factory = sqlite3.Row
    return g.sqlite_db

@app.teardown_appcontext
def close_db(error):
    if hasattr(g, 'sqlite_db'):
        g.sqlite_db.close()

上述代码定义了一个get_db()函数,用于获取数据库连接。其中,g是Flask应用上下文对象,可用于共享数据。另外,还定义了一个close_db()函数,用于关闭数据库连接。

示例1:博客应用

下面我来举一个简单的示例,说明如何使用Flask开发一个博客应用。

创建数据库

首先,需要创建一个SQLite数据库,保存文章的标题和内容。可以使用下面的SQL语句创建表:

create table entries (
  id integer primary key autoincrement,
  title string not null,
  content string not null
);

编写模板

然后,需要编写一个模板用于渲染文章列表和文章详情页面。可以在templates文件夹下编写一个base.html和一个index.html模板。

base.html模板如下所示:

<!doctype html>
<html>
  <head>
    <title>{% block title %}{% endblock %}</title>
  </head>
  <body>
    <div class="content">
      {% block content %}{% endblock %}
    </div>
  </body>
</html>

index.html模板如下所示:

{% extends "base.html" %}
{% block content %}
  <h1>Article List</h1>
  {% for entry in entries %}
    <h2><a href="{{ url_for('entry', id=entry['id']) }}">{{ entry['title'] }}</a></h2>
    <p>{{ entry['content'] }}</p>
  {% endfor %}
{% endblock %}

编写视图函数

然后,需要编写视图函数,用于获取文章数据,渲染模板并处理表单提交。可以使用下面的代码实现。

from flask import Flask, request, session, g, redirect, url_for, \
     abort, render_template, flash, jsonify
import sqlite3

app = Flask(__name__)

# 数据库配置
DATABASE = './entries.db'
DEBUG = True
SECRET_KEY = 'development key'
USERNAME = 'admin'
PASSWORD = 'default'

app.config.from_object(__name__)

def connect_db():
    return sqlite3.connect(app.config['DATABASE'])

# 创建数据库表
def init_db():
    with app.app_context():
        db = get_db()
        with app.open_resource('schema.sql', mode='r') as f:
            db.cursor().executescript(f.read())
        db.commit()

# 获取数据库连接
def get_db():
    if not hasattr(g, 'sqlite_db'):
        g.sqlite_db = connect_db()
        g.sqlite_db.row_factory = sqlite3.Row
    return g.sqlite_db

# 关闭连接
@app.teardown_appcontext
def close_db(error):
    if hasattr(g, 'sqlite_db'):
        g.sqlite_db.close()

# 首页,显示文章列表
@app.route('/')
def index():
    db = get_db()
    cur = db.execute('select title, content from entries order by id desc')
    entries = cur.fetchall()
    return render_template('index.html', entries=entries)

# 显示文章详情
@app.route('/entry/<int:id>')
def entry(id):
    db = get_db()
    cur = db.execute('select title, content from entries where id = ?', [id])
    entry = cur.fetchone()
    if entry is None:
        abort(404)
    return render_template('entry.html', entry=entry)

# 添加文章
@app.route('/add', methods=['POST'])
def add():
    if not session.get('logged_in'):
        abort(401)
    db = get_db()
    db.execute('insert into entries (title, content) values (?, ?)',
                 [request.form['title'], request.form['content']])
    db.commit()
    flash('New entry was successfully posted')
    return redirect(url_for('index'))

# 登录
@app.route('/login', methods=['GET', 'POST'])
def login():
    error = None
    if request.method == 'POST':
        if request.form['username'] != app.config['USERNAME']:
            error = 'Invalid username'
        elif request.form['password'] != app.config['PASSWORD']:
            error = 'Invalid password'
        else:
            session['logged_in'] = True
            flash('You were logged in')
            return redirect(url_for('index'))
    return render_template('login.html', error=error)

# 注销
@app.route('/logout')
def logout():
    session.pop('logged_in', None)
    flash('You were logged out')
    return redirect(url_for('index'))

if __name__ == '__main__':
    app.run()

这段代码定义了4个路由,分别用于显示文章列表,显示文章详情,添加文章和登录。

启动应用

最后,使用flask run命令启动应用,访问http://localhost:5000即可查看效果。

示例2:电影推荐应用

下面我来举另一个简单的示例,说明如何使用Flask开发一个电影推荐应用。

数据集和模型

首先,需要准备一个电影的评分数据集和基于该数据集训练的推荐模型。可以使用MovieLens数据集,下载地址为:https://grouplens.org/datasets/movielens/

基于MovieLens数据集可以训练出多种推荐模型,如基于用户的协同过滤、基于物品的协同过滤、矩阵分解等。这里我们使用矩阵分解模型,代码如下所示:

import numpy as np
import pandas as pd
import os
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

class MatrixFactorization:
    def __init__(self, n_factors=10, alpha=0.01, lamda=0.1, n_epochs=10, random_state=42):
        self.n_factors = n_factors
        self.alpha = alpha
        self.lamda = lamda
        self.n_epochs = n_epochs
        self.random_state = random_state

    def fit(self, X):
        users = X['userId'].unique()
        items = X['movieId'].unique()
        n_users = users.shape[0]
        n_items = items.shape[0]
        user_idx = dict(zip(users, range(n_users)))
        item_idx = dict(zip(items, range(n_items)))

        X['userId'] = X['userId'].apply(lambda x: user_idx[x])
        X['movieId'] = X['movieId'].apply(lambda x: item_idx[x])

        U = np.random.normal(size=(n_users, self.n_factors))
        V = np.random.normal(size=(n_items, self.n_factors))
        for epoch in range(self.n_epochs):
            train_X, valid_X = train_test_split(X)
            for i, x in train_X.iterrows():
                u = x['userId']
                v = x['movieId']
                r = x['rating']
                error = r - np.dot(U[u], V[v])
                U[u] += self.alpha * (error * V[v] - self.lamda * U[u])
                V[v] += self.alpha * (error * U[u] - self.lamda * V[v])
            y_true = valid_X['rating'].values
            y_pred = np.array([np.dot(U[x['userId']], V[x['movieId']]) for i, x in valid_X.iterrows()])
            rmse = np.sqrt(mean_squared_error(y_true, y_pred))
            print('Epoch:', epoch, 'RMSE:', rmse)
        self.U = U
        self.V = V

    def predict(self, user, item):
        return np.dot(self.U[user], self.V[item])

以上代码定义了一个MatrixFactorization类,用于训练矩阵分解模型。

Flask应用

接下来,我们需要使用Flask构建一个推荐应用,并提供用户界面和推荐API。

应用的文件夹结构如下所示:

movie_recommender/
|- app/
|  |- templates/
|  |  |- index.html
|  |- __init__.py
|- data/
|  |- ratings.csv
|  |- movies.csv
|- model/
|  |- model.pkl
|- tests/
|  |- test_model.py
|- Dockerfile
|- docker-compose.yml
|- requirements.txt
|- run.py

其中,app文件夹下包含应用的代码,data文件夹下包含训练数据,model文件夹下包含训练好的模型。

编写视图函数

下面的代码定义了一个推荐应用,提供电影评分界面和基于矩阵分解模型的电影推荐API。

from flask import Flask, request, jsonify, render_template
import pandas as pd
import numpy as np
import joblib
import os
from app.my_model import MatrixFactorization

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/predict', methods=['POST'])
def predict():
    userId = int(request.form['userId'])
    movieId = int(request.form['movieId'])
    rating = float(request.form['rating'])
    print(userId, movieId, rating)

    # 加载模型
    model_path = os.path.join(os.getcwd(), 'model', 'model.pkl')
    model = joblib.load(model_path)

    # 预测评分
    score = model.predict(userId, movieId)
    print(score)

    # 更新模型
    data_path = os.path.join(os.getcwd(), 'data', 'ratings.csv')
    X = pd.read_csv(data_path)
    X = X.append({'userId': userId, 'movieId': movieId, 'rating': rating}, ignore_index=True)
    model.fit(X)
    joblib.dump(model, model_path)

    # 返回结果
    return jsonify({'score': round(score, 2)})

以上的代码定义了两个路由,一个用于渲染页面,在页面中展示电影列表和评分表单;另一个用于接收表单提交,预测评分并更新模型。

编写模型训练脚本

训练矩阵分解模型的代码如下所示:

import pandas as pd
import numpy as np
import joblib
import os
from app.my_model import MatrixFactorization

# 加载数据
data_path = os.path.join(os.getcwd(), 'data', 'ratings.csv')
X = pd.read_csv(data_path)

# 训练模型
model = MatrixFactorization(n_factors=10, alpha=0.01, lamda=0.1, n_epochs=10)
model.fit(X)

# 保存模型
model_path = os.path.join(os.getcwd(), 'model', 'model.pkl')
joblib.dump(model, model_path)

以上代码定义了一个脚本,用于加载数据,训练模型并保存训练好的模型。

总结

以上就是使用Flask开发项目的完整攻略。Flask是一个轻量的Web框架,易于上手,支持多种数据库和模板引擎,并且非常灵活。使用Flask,可以快速实现各种Web应用,也可以作为学习Web开发的入门工具。