创建项目

首先打开 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 所示。

image 2025 01 21 20 47 04 079
Figure 1. 图9-2 PyCharm专业版创建pythonbbs项目

虚拟环境是一个独立的 Python 环境,安装的包不会影响其他环境,其他环境安装的包也不会影响虚拟环境。

单击 Create 按钮后即可创建 pythonbbs 项目,pythonbbs 项目的结构如图 9-3 所示。

image 2025 01 21 20 47 48 140
Figure 2. 图9-3 pythonbbs项目结构

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

image 2025 01 21 20 48 35 050
Figure 3. 图9-4 PyCharm中安装flask-sqlalchemy的步骤

之所以在 PyCharm Terminal 中安装包,是因为我们现在用了项目中的虚拟环境,直接打开 PyCharm Terminal 会进入虚拟环境中,此时通过 pip install 命令安装的包会安装在项目的虚拟环境中。如果在 cmd(命令行终端)中安装,则会安装在系统的 Python 环境中,项目的虚拟环境和系统的 Python 环境是两个独立的环境,互不影响。

项目需要提前安装的包,包括但不限于以下所示的包。

  1. pymysql

    • 安装命令:pip install pymysql。

    • 作用:Python操作数据库的驱动程序。

  2. Flask-SQLAlchemy

    • 安装命令:pip install flask-sqlalchemy。

    • 作用:用于在Flask中使用ORM模型操作数据库。

  3. cryptography

    • 安装命令:pip install cryptography。

    • 作用:对密码加密和解密。

  4. 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 所示。

image 2025 01 21 20 58 59 956
Figure 4. 图9-5 创建pythonbbs数据库

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 所示。

image 2025 01 21 21 01 30 547
Figure 5. 图9-6 循环引用
image 2025 01 21 21 02 38 948
Figure 6. 图9-7 使用exts.py打破循环引用

这里使用 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 所示。

image 2025 01 21 21 06 47 726
Figure 7. 图9-8 创建blueprints包

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

image 2025 01 21 21 07 29 335
Figure 8. 图9-9 创建blueprints后的项目结构图

接下来继续完善蓝图,分别在 cms.py、front.py、user.py 中创建蓝图对象,相关文件代码如下。

cms.py
from flask import Blueprint

bp = Blueprint("cms",__name__,url_prefix="/cms")
front.py
from flask import Blueprint

bp = Blueprint("front",__name__,url_prefix="")
user.py
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!'

models模块

为了让项目更加简洁,我们把所有的 ORM 模型也进行模块化,按照如图 9-8 所示的方法,在 pythonbbs 根路径下创建一个名为 models 的 Python Package,然后在 models 下分别创建 user.py 和 post.py 文件,这两个文件分别用来存放与用户和帖子相关的 ORM 模型。ORM 模型后续再添加,models 模块结构如图 9-10 所示。

image 2025 01 21 21 14 45 927
Figure 9. 图9-10 models模块结构

到目前为止,pythonbbs 项目结构就已经搭建好了。下面再讲解实现每个功能的技术细节。