执行 flask run 命令会返回一个错误提示。这是因为 Flask 默认会假设你把程序存储在名为 app.py 或 wsgi.py 的文件中。如果你使用了其他名称,就要设置系统环境变量 FLASK_APP 来告诉 Flask 你要启动哪个程序:

现在在启动 Flask 程序的时候,我们通常要和两个环境变量打交道:FLASK_APPFLASK_DEBUG

为了不用每次打开新的终端会话都要设置环境变量,我们安装用来自动导入系统环境变量的 python-dotenv:

当 python-dotenv 安装后,Flask 会从项目根目录的 .flaskenv 和 .env 文件读取环境变量并设置。我们分别使用文本编辑器创建这两个文件,或是使用更方便的 touch 命令创建(注意不要漏掉文件名开头的点):

1
$ touch .env .flaskenv

.flaskenv 用来存储 Flask 命令行系统相关的公开环境变量;而 .env 则用来存储敏感数据,不应该提交进 Git 仓库,我们把文件名 .env 添加到 .gitignore 文件的结尾(新建一行)来让 Git 忽略它。你可以使用编辑器打开这个文件,然后添加下面这一行内容:

1
.env

在新创建的 .flaskenv 文件里,我们写入一行 FLASK_DEBUG=1,将环境变量 FLASK_DEBUG 的值设为 1,以便开启调试模式:

1
2
# .flaskenv 文件
FLASK_DEBUG=1

除此之外,它还有一个重要的作用:作为代表某个路由的端点(endpoint),同时用来生成视图函数对应的 URL。对于程序内的 URL,为了避免手写,Flask 提供了一个 url_for 函数来生成 URL,它接受的第一个参数就是端点值,默认为视图函数的名称:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from flask import url_for
from markupsafe import escape

# ...

@app.route('/')
def hello():
return 'Hello'

@app.route('/user/<name>')
def user_page(name):
return f'User: {escape(name)}'

@app.route('/test')
def test_url_for():
# 下面是一些调用示例(请访问 http://localhost:5000/test 后在命令行窗口查看输出的 URL):
print(url_for('hello')) # 生成 hello 视图函数对应的 URL,将会输出:/
# 注意下面两个调用是如何生成包含 URL 变量的 URL 的
print(url_for('user_page', name='greyli')) # 输出:/user/greyli
print(url_for('user_page', name='peter')) # 输出:/user/peter
print(url_for('test_url_for')) # 输出:/test
# 下面这个调用传入了多余的关键字参数,它们会被作为查询字符串附加到 URL 后面。
print(url_for('test_url_for', num=2)) # 输出:/test?num=2
return 'Test page'

我们把包含变量和运算逻辑的 HTML 或其他格式的文本叫做模板,执行这些变量替换和逻辑计算工作的过程被称为渲染,这个工作由我们这一章要学习使用的模板渲染引擎——Jinja2 来完成。

按照默认的设置,Flask 会从程序实例所在模块同级目录的 templates 文件夹中寻找模板,我们的程序目前存储在项目根目录的 app.py 文件里,所以我们要在项目根目录创建这个文件夹:

Flask 也可以直接展示 HTML,实现动态,这时候可以把变量通过某种语法放到 HTML 中,然后通过 jinja2 这个工具,render_template 来进行动态的渲染。

为了设置 Flask、扩展或是我们程序本身的一些行为,我们需要设置和定义一些配置变量。Flask 提供了一个统一的接口来写入和获取这些配置变量:Flask.config 字典。配置变量的名称必须使用大写,写入配置的语句一般会放到扩展类实例化语句之前。

下面写入了一个 SQLALCHEMY_DATABASE_URI 变量来告诉 SQLAlchemy 数据库连接地址:

1
2
3
4
5
import os

# ...

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////' + os.path.join(app.root_path, 'data.db')

注意 这个配置变量的最后一个单词是 URI,而不是 URL。

对于这个变量值,不同的 DBMS 有不同的格式

app.root_path 返回程序实例所在模块的路径

另外,如果你使用 Windows 系统,上面的 URI 前缀部分只需要写入三个斜线(即 sqlite:///)。在本书的示例程序代码里,做了一些兼容性处理,另外还新设置了一个配置变量,实际的代码如下:

app.py:数据库配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import os
import sys

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

WIN = sys.platform.startswith('win')
if WIN: # 如果是 Windows 系统,使用三个斜线
prefix = 'sqlite:///'
else: # 否则使用四个斜线
prefix = 'sqlite:////'

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = prefix + os.path.join(app.root_path, 'data.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # 关闭对模型修改的监控
# 在扩展类实例化前加载配置
db = SQLAlchemy(app)

模型类创建后,还不能对数据库进行操作,因为我们还没有创建表和数据库文件。下面在 Python Shell 中创建了它们:

1
2
3
(env) $ flask shell
>>> from app import db
>>> db.create_all()

如果你改动了模型类,想重新生成表模式,那么需要先使用 db.drop_all() 删除表,然后重新创建:

1
2
>>> db.drop_all()
>>> db.create_all()

注意这会一并删除所有数据,如果你想在不破坏数据库内的数据的前提下变更表的结构,需要使用数据库迁移工具,比如集成了 AlembicFlask-Migrate 扩展。

Flask 还可以编写自定义命令,来自动执行创建数据库表等操作: