23 数据库应用(三):项目数据库配置实战
你好,我是Barry。
在上节课,我们实现了数据库表的创建,也实现了项目和数据库的联动,这就相当于架设好了项目和数据库的桥梁。不过,我们前面都做的都是单点功能的实践。
为了让你加深理解,今后轻松应用数据库来完成项目的数据管理和业务处理,接下来的两节课我会带你完成数据库配置,并结合项目综合应用数据库。
在学习数据库配置前,我们需要先来了解一下项目层级关系。只有明确项目的层级关系,数据库配置的脉络才会更加清晰,也能为我们之后的项目实战做好铺垫。
项目层级框架
我们先从项目层级框架开始说起。Flask 项目通常分为三层,我们分别来看看。
- 第一层是前端展示层,包括 HTML、CSS、JS 等文件,用于展示用户界面和与用户交互。这也是直接与用户接触的内容。
- 第二层是后端业务逻辑层,用来获取数据库的数据并进行业务逻辑处理,最后把处理好的数据交给前端去渲染展示。这层通常会涉及到 ORM 对象关系映射层,用来和数据库交互。
- 第三层就是数据库层,作用是存储和管理数据,通常会用到MySQL这样的关系型数据库。
当然,你也要明确一个 Flask 项目并不是都需要分为三层。具体的层级分布取决于项目的复杂度和需求。有些项目可能只需要一个简单的展示层和业务逻辑层,而有些项目可能需要更多的层次,才能支持更复杂的业务逻辑和数据管理。
随着项目变得越来越庞大,组织管理各个文件会越来越困难。为了更好地组织代码,我们会给每个项目设置许多文件夹,用来存放不同的内容。
在我们的在线直播视频平台项目中,这三个层级的代码都要放在api文件夹下。接下来,我们明确一下文件模块具体如何管理。前端资源放在templates文件夹下,ORM放在models文件夹下,后端业务逻辑层放在modules文件夹下,而数据库的基本信息配置就放在config文件中。
数据库的配置
梳理好了层级关系,我们这就一起来看看在项目中如何实现对数据库的应用。
数据库核心配置项
前面说了,数据库的配置我们要对应放在config文件下。所以我们第一步要做的,就是直接在config文件下新建config.py。
第二步就是配置数据库的一些基本信息。其中包含我们的数据库IP地址、端口以及用户名和密码等相关信息,具体的代码如下所示。
connection = pymysql.connect(
host='127.0.0.1', # 数据库IP地址
port=3306, # 端口
user='root', # 数据库用户名
password='flask_project', # 数据库密码
database='my_project' # 数据库名称
)
这里我们将配置信息封装到一个类中,这样的好处是让代码更易维护和分离,而且这样做还能方便我们配置不同环境,比如开发环境、测试环境和生产环境。此外为了保证数据的安全性,类中还可以存储敏感信息,比如数据库用户名和密码这些信息。
后面是具体的配置代码,供你参考。
class Config:
SQL_HOST = '127.0.0.1'
SQL_USERNAME = 'root'
SQL_PASSWORD = 'flask_project'
SQL_PORT = 3306
SQL_DB = 'my_project'
JSON_AS_ASCII = False
# 数据库的配置
SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{SQL_USERNAME}:{SQL_PASSWORD}@{SQL_HOST}:{SQL_PORT}/{SQL_DB}"
SQLALCHEMY_TRACK_MODIFICATIONS = False
对照前面的代码,我们来梳理一下这些配置项的具体含义。
掌握了前面这些数据库核心配置项之后,你就能轻松地实现连接配置了。常规的开发流程里,项目包括开发、测试、生产这三个阶段,所以我们为了配置的方便,可以事先定义好这三个类。
# 开发环境的config
class DevConfig(Config):
pass
# 生产环境的config
class ProConfig(Config):
DEBUG = False
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:my_project@127.0.0.1:3306/aaa"
# 测试环境的config
class TestConfig(Config):
pass
config_dict = {
'dev': DevConfig,
'pro': ProConfig,
'test': TestConfig,
}
这三个类均继承了Config类。同时,为了方便我们的转换,我们还要定义一个字典config_dict,用它来设置这三个字段。需要注意的是,针对项目的不同阶段,数据库中的数据是不一样的,只有这样我们才能实现对不同阶段代码的灵活调试。
通过SQLAlchemy实现ORM应用
搞定了数据库配置项之后,我们下面就通过SQLAlchemy来实现ORM的应用。上节课讲过,QLAlchemy是一个Python编程语言下的SQL工具包和ORM库,它提供了SQL表达式操作和ORM映射操作的工具。
同理,在我们项目开发中也是一样的流程。我们先来安装SQLAlchemy,具体的执行命令如下所示。
安装成功之后我们还需要完成实例化,实例化之后才能将一些全局配置应用到具体的数据库操作中。
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
db = SQLAlchemy(app)
在项目当中,我们一般把db和app的实例化,放在api文件夹下的__init__.py文件中。
具体代码是后面这样,你可以边看代码,边听我为你梳理这段代码的用途。
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config.config import config_dict
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__)
config = config_dict.get(config_name)
app.config.from_object(config)
db.init_app(app)
return app
这段代码我们重点要关注create_app函数。我们一般把create_app函数称为工厂函数。在create_app函数中我们可以传入一个config_name参数,方便我们切换开发、测试的数据库配置。
其中app.config.from_object(config)的作用是将config对象中定义的配置参数应用到Flask应用程序中,包括数据库连接信息、密钥、调试模式等等。
db.init_app(app)则是将Flask应用程序对象传递给SQLAlchemy的db对象,并且绑定这两个对象,这样就能通过SQLAlchemy的db对象来处理与数据库有关的操作(如创建模型、查询数据、执行事务等等)了。
建立数据库表
完成了前面的各种配置,我们这就来建立数据库表。首先,我们需要在models文件夹中新建一个base.py文件,这里主要用来存放我们的模型基类。
类的创建
模型基类可以用于定义数据库表的结构和关系。在模型中,我们可以定义表的列、主键、外键等,并使用其提供的方法执行数据库操作。这样模型基类就可以通过导入的方式,被其他文件引用。具体的代码实现是后面这样。
from datetime import datetime
from api import db
class BaseModels:
"""模型基类"""
create_time = db.Column(db.DateTime, default=datetime.now) # 创建时间
update_time = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now) # 记录你的更新时间
status = db.Column(db.SmallInteger, default=1) # 记录存活状态
下一步,我们需要在models文件下新建user.py文件,用于存放用户信息表。
from api import db
from api.models.base import BaseModels
class UserInfo(BaseModels, db.Model):
"""用户信息表"""
__tablename__ = "user_info"
id = db.Column(db.Integer, primary_key=True, autoincrement=True) # 用户id
nickname = db.Column(db.String(64), nullable=False) # 用户昵称
mobile = db.Column(db.String(16)) # 手机号
avatar_url = db.Column(db.String(256)) # 用户头像路径
signature = db.Column(db.String(256)) # 签名
sex = db.Column(db.Enum('0', '1', '2'), default='0') # 1男 2 女 0 暂不填写
birth_date = db.Column(db.DateTime) # 出生日期
role_id = db.Column(db.Integer) # 角色id
last_message_read_time = db.Column(db.DateTime)
def to_dict(self):
return {
'id': self.id,
'nickname': self.nickname,
'mobile': self.mobile,
'avatar_url': self.avatar_url,
'sex': self.sex,
}
这里的to_dict函数可以将 UserInfo 对象序列化为 JSON 数据,这样就能在查询时将查询到的数据放在 Flask 的响应中。这里的学习重点就是掌握创建方法,后面随着项目的深入,我们还会根据项目规模在models文件下灵活添加其他的文件。
生成数据库表
在创建数据库表类之后,数据库表的创建还差最后一个环节——执行代码生成数据库表。
首先,我们需要在项目的根目录下新建app.py文件。app.py文件能将多个Flask脚本整合到一个文件中,简化Flask应用程序的管理和部署。
我们只要在app.py文件下做好相关函数的导入和初始化工作,就可以把app.py作为Flask的命令行接口,通过它运行不同的Flask命令,高效完成数据库迁移、创建超级用户、导出数据等操作。
紧接着,我们需要在api文件中导入db和create_app工厂函数。
from api import db, create_app
from flask_script import Manager
from flask_migrate import Migrate
app = create_app('dev')
# 管理器对象可以用来提供各种管理命令,例如启动应用程序、创建数据库表、导入数据等等
manager = Manager(app)
Migrate(app, db)
def create_db():
db.create_all()
if __name__ == '__main__':
manager.run()
对照前面的代码,我来讲解一下重点。我们导入了所需的库和模块。api 模块中包含了 Flask 应用程序和数据库连接,create_app 函数用来创建应用程序实例,Manager 类用于创建管理命令的对象,Migrate 类用于处理数据库迁移。
之后,我们需要使用 create_app 函数创建一个名为 dev 的开发环境应用程序实例。同时使用 Manager 类创建一个名为 manager 的管理命令对象,该对象可以用来提供各种管理命令,例如启动应用程序、创建数据库表、导入数据等等。
第七行代码表示我们用Migrate 类来初始化 Flask-Migrate,目的是方便处理数据库迁移。
之后第八行代码用到了create_db 函数,它的主要作用是使用 db.create_all() 方法创建所有定义的数据库表。
最后是第十一行代码,我们在主程序中,使用 manager.run() 方法运行管理命令对象,目的是启动应用程序并监听管理命令。
理解了前面这些执行代码后,我们还需要生成migrations 文件,这是为了方便后面变更和管理数据库结构,防止出现误操作或异常操作。执行命令和对应的执行效果图是后面这样。执行后面的语句后,就会自动生成migrations文件夹。
文件夹中包含文件versions,未来每次修改数据库表结构时,这个文件都会生成一个脚本文件,用来记录每次数据库表的变化。其他的文件都是一些配置文件,你暂时不需要关心。
自动生成migrations文件夹后,我们还要在终端中输入后面的命令,命令的作用你可以参考代码注释。
命令输入后,我们打开Navicat,就会发现my_project数据库中多出了一个alembic_version数据库表。
这个表中只有一个字段,是用来存储版本号。我们可以通过查询alembic_version表的版本号来确定当前数据库所处的状态。此外,alembic_version表还可以用于在不同版本之间实现数据库迁移操作。
完成前面这些初步操作之后,我们还需要创建user_info数据库表。具体操作就是在pycharm的Terminal终端,输入后面的命令。
这里我们输入Flask Shell,就会进入一个交互式的Python编程,自由使用所有的Flask应用程序中定义的变量、函数和对象以及数据库模型、路由和视图函数等等。这个环节一般用来测试和调试应用程序。
进入交互式命令后,还需要输入db.create_all()来创建所有的数据库表。执行完成后,只需要Ctrl+Z退出执行即可。此时我们再次打开Navicat,就会看到数据库表user_info已经创建好了。
不过仔细看一下就会发现,此时user_info中还没有任何信息,因为我们目前只完成了表的创建,还没有新增数据,这部分内容下节课我们再继续探索。
总结
又到了课程的尾声,我们来回顾一下今天的学习重点。
想要在项目中灵活应用数据库,熟悉项目层级框架并做好每个模块的配置管理这些准备步骤非常重要。通常一个 Flask 项目会分成三层:前端展示层、后端业务逻辑层和数据库层,明确了它们的作用,我们才能更好地管理项目。
对比前面课程的单点实践,你会发现今天在数据库配置阶段,有些配置存在一些细微变动,这些只有落地项目的时候才能感受到。这个过程你需要重点关注create_app函数的用法,尤其是其中各个参数的作用,这样才能高效配置数据库。
另一个学习重点就是数据库表建立。结合user.py文件的案例,我们学习了如何通过db.Model模型基类中的db.Column类创建数据库。你需要明确app.py文件在项目中的定位和作用,并且牢牢掌握数据库表创建的实现代码,把握其思路,这样才能从容应对类似的建表需求。
思考题
Flask项目开发包括开发、测试、生产这三个阶段,你觉得是否需要对应有三个数据库来支撑开发?还是说单个数据库就可以满足?
欢迎你在留言区和我交流互动,如果这节课的内容对你有帮助,别忘了分享给身边更多朋友。
- 长林啊 👍(1) 💬(1)
在项目开发中,生产环境的数据库肯定是独立的,开发和测试可以使用同一个数据库;在开发和测试过程中,会添加很多测试数据,对于生产环境而言,这部分是脏数据且没有任何实际意义
2023-06-20 - Geek_7e5830 👍(0) 💬(1)
好像python3.8以上会报 from flask_sqlalchemy import SQLAlchemy File "C:\PythonEnv\VideoPlateform\.venv\Lib\site-packages\flask_sqlalchemy\__init__.py", line 39, in <module> _timer = time.clock ^^^^^^^^^^ AttributeError: module 'time' has no attribute 'clock'错误。 Python 3.8 版本中移除了 time 模块中的 time.clock() 方法。
2024-05-13 - 高并发 👍(0) 💬(2)
老师都正常执行了, 但是执行完成db.create_all()之后, 数据库中为什么没有表
2024-01-17 - Geek_d26f93 👍(0) 💬(1)
raise RuntimeError( RuntimeError: Either 'SQLALCHEMY_DATABASE_URI' or 'SQLALCHEMY_BINDS' must be set.这个错误怎么办
2024-01-14 - Geek_d26f93 👍(0) 💬(1)
Traceback (most recent call last): File "D:\environment\Python\Miniconda3\envs\venv2\lib\site-packages\flask\cli.py", line 219, in locate_app __import__(module_name) File "D:\environment\Python\pythonProject\First_Flask\app.py", line 2, in <module> from flask_script import Manager File "D:\environment\Python\Miniconda3\envs\venv2\lib\site-packages\flask_script\__init__.py", line 15, in <module> from flask._compat import text_type ModuleNotFoundError: No module named 'flask._compat' 请问我代码遗漏了哪部分会出现这个错误
2024-01-13 - Aegean Sea 👍(0) 💬(1)
(venv2) PS D:\environment\Python\pythonProject\First_Flask> flask db init Error: While importing 'app', an ImportError was raised: Traceback (most recent call last): File "D:\environment\Python\Miniconda3\envs\venv2\lib\site-packages\flask\cli.py", line 219, in locate_app __import__(module_name) File "D:\environment\Python\pythonProject\First_Flask\app.py", line 2, in <module> from flask_script import Manager File "D:\environment\Python\Miniconda3\envs\venv2\lib\site-packages\flask_script\__init__.py", line 15, in <module> from flask._compat import text_type ModuleNotFoundError: No module named 'flask._compat' Usage: flask [OPTIONS] COMMAND [ARGS]... Try 'flask --help' for help. Error: No such command 'db'. 老师求解,试了很多方法都行不通
2023-10-28 - Aegean Sea 👍(0) 💬(1)
ModuleNotFoundError: No module named 'flask._compat' Usage: flask [OPTIONS] COMMAND [ARGS]... Try 'flask --help' for help. Error: No such command 'db'. 老师求解。试了很多方法行不通
2023-10-28 - Geek_00c9c4 👍(0) 💬(1)
你好,项目中使用的每个包的版本是多少呀(能否发一下你用的版本)?执行flask db init ,报这个错误, Traceback (most recent call last): File "/Users/kevin/.pyenv/versions/3.12.0/bin/flask", line 5, in <module> from flask.cli import main File "/Users/kevin/.pyenv/versions/3.12.0/lib/python3.12/site-packages/flask/__init__.py", line 19, in <module> from jinja2 import Markup, escape ImportError: cannot import name 'Markup' from 'jinja2' (/Users/kevin/.pyenv/versions/3.12.0/lib/python3.12/site-packages/jinja2/__init__.py)
2023-10-19 - Geek_5cb928 👍(0) 💬(1)
在执行flask shell后面的db相关命令时报错,name 'db' is not defined 发现造成的原因是教程里面用到的pycharm会自动配置context,用vscode所以遇到了这个问题。 修复如下 在app.py (manager.py)下面定义context @app.shell_context_processor def make_shell_context(): return {'db': db} 供大家参考
2023-08-29 - 蜡笔小新爱看书 👍(0) 💬(2)
db.Column,输入.pycharm带不出来Column的提示,是什么原因?
2023-08-17 - Geek_7cc417 👍(0) 💬(1)
我想问一下老师,在本次项目中,我们数据库设计只有一个用户信息表就够了吗?我想学习一下数据库怎么设计的,来修改增加去做个毕业设计不知道能不能行
2023-07-16 - 🌀Pick Monster 🌀 👍(0) 💬(1)
flask db init 报错Error: No such command 'db'. 提示在api __init__中没有db,这个为什么
2023-07-05 - 未来已来 👍(0) 💬(2)
不知道为啥,执行完 `flask shell` 及 `db.create_all()`之后,没有正常建表
2023-06-29 - Geek_840593 👍(0) 💬(2)
老师:app.py这里不是有段代码创建数据库表 。。。。。。 def create_db(): db.create_all() if __name__ == '__main__': manager.run() 为什么我们还是要手动在Terminal输入一次: flask shell db.create_all()
2023-06-21 - peter 👍(0) 💬(2)
Q1:前缀f是什么意思? SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{SQL_USERNAME}:{SQL_PASSWORD}@{SQL_HOST}:{SQL_PORT}/{SQL_DB}", 字符串前面的前缀f表示什么? Q2:pass是什么意思? # 开发环境的configclass DevConfig(Config): pass 函数体的pass是什么意思?省略吗?
2023-06-15