创建项目
首先打开 PyCharm 专业版,选择 Flask 项目,然后填写项目路径,并且选择 PythonInterpreter 为 New Virtualenv environment,也就是为本项目创建一个虚拟环境,这和第 1 章所提倡的用 Previously configured interpreter(以下简称 Previously)不同。之前提倡用 Previously 的原因是,避免每次创建学习项目时都要重新安装 Flask。而现在创建的 pythonbbs 项目在开发完成后,需要部署到 Linux 服务器上运行,在部署前可以通过 pip freeze→requirements.txt 命令将 pythonbbs 项目依赖的包及其版本号全部导出来,方便后期部署,如果使用 Previously,则会把一些不需要的包也导出来。下面使用 PyCharm 专业版创建 pythonbbs 项目,如图 9-2 所示。

虚拟环境是一个独立的 Python 环境,安装的包不会影响其他环境,其他环境安装的包也不会影响虚拟环境。 |
单击 Create 按钮后即可创建 pythonbbs 项目,pythonbbs 项目的结构如图 9-3 所示。

创建完项目后,PyCharm 会默认在当前项目的虚拟环境中安装 Flask,但是其他第三方插件是没有安装的,因此我们需要先把后期开发中会用到的包提前安装好。安装方法为打开 PyCharm 界面底部的 Terminal,然后输入安装命令。以安装 flask-sqlalchemy 为例,操作步骤如图 9-4 所示。

之所以在 PyCharm Terminal 中安装包,是因为我们现在用了项目中的虚拟环境,直接打开 PyCharm Terminal 会进入虚拟环境中,此时通过 pip install 命令安装的包会安装在项目的虚拟环境中。如果在 cmd(命令行终端)中安装,则会安装在系统的 Python 环境中,项目的虚拟环境和系统的 Python 环境是两个独立的环境,互不影响。 |
项目需要提前安装的包,包括但不限于以下所示的包。
-
pymysql
-
安装命令:pip install pymysql。
-
作用:Python操作数据库的驱动程序。
-
-
Flask-SQLAlchemy
-
安装命令:pip install flask-sqlalchemy。
-
作用:用于在Flask中使用ORM模型操作数据库。
-
-
cryptography
-
安装命令:pip install cryptography。
-
作用:对密码加密和解密。
-
-
Flask-Migrate
-
安装命令:pip install flask-migrate。
-
作用:用于将ORM模型的变更同步到数据库中。
-
包安装完后,我们以大型项目为标准完善项目结构,把程序框架搭建起来。
config.py文件
在 pythonbbs 项目根路径下创建一个名叫 config.py 的 Python 文件,这个文件用来存放配置项。在项目运行过程中,会根据环境选择不同的配置。如以数据库连接配置为例,在开发时,可能连接的是开发环境的数据库;在测试时,可能连接的是测试服务器的数据库;而在上线后,则需要更换成线上服务器的数据库。为了满足不同环境下不同的配置,我们在 config.py 文件中根据环境创建不同的类,来分别实现具体的配置。如开发环境则创建 DevelopmentConfig 类,测试环境则创建 TestingConfig 类,线上环境则创建 ProductionConfig 类。还有一些在任何环境下都相同的配置项,我们再为其创建一个 BaseConfig 类,让以上 3 个类继承即可。config.py 文件中的代码如下。
class BaseConfig:
SECRET_KEY = "your secret key"
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopmentConfig(BaseConfig):
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:root@127.0.0.1:3306/pythonbbs?charset=utf8mb4"
class TestingConfig(BaseConfig):
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://[测试服务器MySQL用户名]:[测试服务器MySQL密码]@[测试服务器MySQL域名]:[测试服务器MySQL端口号]/pythonbbs?charset=utf8mb4"
class ProductionConfig(BaseConfig):
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://[生产环境服务器MySQL用户名]:[生产环境服务器MySQL密码]@[生产环境服务器MySQL域名]:[生产环境服务器MySQL端口号]/pythonbbs?charset=utf8mb4"
接下来在 app.py 文件中,根据当前环境选择不同的配置类即可。这里以开发环境为例,app.py 文件中绑定配置的代码如下。
from flask import Flask
import config
app = Flask(__name__)
app.config.from_object(config.DevelopmentConfig)
在上述代码的 DevelopmentConfig 中,配置了数据库连接 SQLALCHEMY_DATABASE_URI,读者可以根据自身情况选择 MySQL 服务器的域名、端口号、用户名、密码。此外,还需要先在 MySQL 数据库中创建 pythonbbs 数据库,在创建数据库时,选择字符集为 utf8mb4,如图 9-5 所示。

exts.py文件
exts.py 文件主要用来存放一些第三方插件的对象,如 SQLAlchemy 对象、Flask-Mail 对象等。为什么要单独创建一个文件用来存放这些对象呢?这样做是为了防止循环引用。以 SQLAlchemy 对象为例,一般会在 app.py 文件中通过以下代码创建一个 db 变量,用于创建 ORM 模型和进行 ORM 操作。
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy(app)
在项目越来越复杂的情况下,为了保持项目的可维护性,通常会把 ORM 模型放到其他文件中,而创建 ORM 模型又需要 db 变量,因此需要从 app.py 中导入 db 变量,而为了让 ORM 模型能被映射到数据库中,又需要把 ORM 模型直接或者间接导入 app.py,这样就产生了循环引用,如图 9-6 所示。
循环引用会导致项目运行失败。为了打破循环引用,只要在两者中间加一个 exts.py 文件,把会引起循环引用的变量(如 db 变量)放到 exts.py 中,然后其他文件都从 exts.py 中导入变量。这里还是以 db 变量为例,在添加 exts.py 后,三者的关系如图 9-7 所示。


这里使用 Flask-SQLAlchemy 插件来创建一个 SQLAlchemy 对象,在 exts.py 中输入以下代码。
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
读者可以看到,上述代码在创建 SQLAlchemy 对象时,并没有传入 app,原因是如果使用 app.py 中的 app 变量,那么又会产生循环引用。此时可以再回到 app.py 中,然后导入 db 变量,再通过 db.init_app(app) 完成初始化,代码如下。
from flask import Flask
import config
from exts import db
app = Flask(__name__)
app.config.from_object(config.DevelopmentConfig)
db.init_app(app)
按以上代码一样可以对 db 变量完成初始化,并且还解决了循环引用的问题。后续其他第三方插件对象,如用于发送邮件的 Flask-Mail 插件的对象,都可以通过类似的方式实现。
blueprints模块
从图 9-1 所示的项目思维导图可知,项目被分成了许多模块。如果把这些模块的视图代码都放到 app.py 中,那么对后期维护将是一场灾难。为了让项目结构更加清晰,我们通常会使用蓝图来模块化,创建一个名叫 blueprints 的包,用于存放蓝图模块。首先在 pythonbbs 项目名称上右击,然后在弹出的快捷菜单中选择 New→Python Package 命令,如图 9-8 所示。

输入 blueprints,即可完成创建。然后在 blueprints 下分别创建名为 cms、front 和 user 的 Python 文件。创建完成后的项目结构如图 9-9 所示。

接下来继续完善蓝图,分别在 cms.py、front.py、user.py 中创建蓝图对象,相关文件代码如下。
from flask import Blueprint
bp = Blueprint("cms",__name__,url_prefix="/cms")
from flask import Blueprint
bp = Blueprint("front",__name__,url_prefix="")
from flask import Blueprint
bp = Blueprint("user",__name__,url_prefix="/user")
3 个文件中都创建了蓝图对象,并且指定了 url 前缀,因为 front 是面向前台的,所以 url 前缀为空。在蓝图对象创建好之后,还需要在 app.py 中完成注册,否则是无法使用的。在 app.py 中注册蓝图的代码如下。
...
from blueprints.cms import bp as cms_bp
from blueprints.front import bp as front_bp
from blueprints.user import bp as user_bp
# 创建 Flask 应用实例
app = Flask(__name__)
# 从配置类中加载配置
app.config.from_object(config.DevelopmentConfig)
# 初始化数据库
db.init_app(app)
# 注册蓝图
app.register_blueprint(cms_bp)
app.register_blueprint(front_bp)
app.register_blueprint(user_bp)
因为在后续开发中,所有前台的视图代码都会放到 front 蓝图中,所以在 app.py 中遗留的 hello_world 相关代码(如下所示),可以直接删除。
@app.route('/')
def hello_world():
return 'Hello World!'