又是新的工作日,老项和小王也是早早来到了公司,准备好电脑,查看了一下小王完成的前一天的任务,老项也开始了自己的讲课时间。
老项说:“今天我们就讲解flask下数据库相关操作,今天讲完以后,你就可以设计数据库模型,然后把项目的数据库部分做起来了。”
小王调皮的说:“来吧,老铁,开始传功吧!我的大脑已经急不可耐!”
数据库三方包的安装和配置
我们知道flask的核心并不包含对数据库的处理,要使用数据库,需要使用第三方包,这里我们选用flask-sqlalchemy,使用flask-sqlalchemy可以实现数据库的orm(Object-Relational Mapping即对象关系映射)操作,可以让我们使用面向对象的方式操作数据库。但是sqlalchemy并不包含数据库引擎,小王的项目需要使用mysql数据库,因此,我们还需要安装mysql的数据库引擎,这里我们选用flask-mysqldb。在虚拟环境下安装截图如下。
安装好两个第三方包后,需要去配置一下数据库。我们暂时把配置代码放在app.py里面。代码如下图。
主要看第六行对数据库配置的代码。我们来看这行代码右边的字符串,第一个mysql是指的数据库类型,这里的数据库类型可以是mysql,oracle,postgres,sqlite。后面的root是指的数据库的用户名,root后面的bb12345表示数据库密码(哎呀,一不小心把本机的数据库密码泄露了,讲到这里,老项暗想)。@后面是连接的服务器地址,这里指本地ip,ip后面的3306是数据库的端口,最后的car_db是本地构建的数据库名称。在使用数据库前,要先安装mysql数据库(安装过程可以参考我们mysql安装的教程,这里不再赘述),然后提前构建好数据库。可以使用navicat构建数据库,也可以使用数据库的指令创建数据库,注意编码方式选择UTF-8,见下图。
除了以上配置外,还有一些有用的配置,如下图。
关于更多的配置和更详细的信息请参考下表。
配置名称 |
配置意义 |
SQLALCHEMY_DATABASE_URI |
连接数据库的URI配置,具体的配置方法请参考上面的说明 |
SQLALCHEMY_BINDS |
一个字典,该字典通过绑定的key连接URI |
SQLALCHEMY_ECHO |
主要用于debug,为True的时候会回显数据库操作的原语 |
SQLALCHEMY_RECORD_QUERIES |
用于禁用或者启用查询记录。查询记录在调试或者测试模式会自动启用。 |
SQLALCHEMY_NATIVE_UNICODE |
用于禁用原生的unicode支持。 |
SQLALCHEMY_POOL_SIZE |
数据库连接池的大小,默认为5 |
SQLALCHEMY_POOL_TIMEOUT |
指定数据库连接池的连接超时时间 |
SQLALCHEMY_POOL_RECYCLE |
指定多久以后自动回收数据库 |
关于配置就先说这么多,下面我们来说一下flask-sqlalchemy中数据库的常用字段和一些选项。
sqlalchemy常用字段,选项
我们知道数据库里面的数据是有不同类型的。而sqlalchemy要对数据库进行映射,也需要对数据库的数据类型进行映射。映射后,sqlalchemy的主要字段类型可以参考下表。
字段类型名 |
对应的Python类型 |
字段说明 |
Integer |
int |
普通整数,一般是 32 位 |
SmallInteger |
int |
取值范围小的整数,一般是 16 位 |
Big Integer |
int 或 long |
不限制精度的整数 |
Float |
float |
浮点数 |
Numeric |
decimal.Decimal |
定点数 |
String |
str |
变长字符串 |
Text |
str |
变长字符串,对较长或不限长度的字符串做了优化 |
Unicode |
unicode |
变长 Unicode 字符串 |
Unicode Text |
unicode |
变长 Unicode 字符串,对较长或不限长度的字符串做了优化 |
Boolean |
bool |
布尔值 |
Date |
datetime.date |
日期 |
Time |
datetime.time |
时间 |
DateTime |
datetime.datetime |
日期和时间 |
Interval |
datetime.timedelta |
时间间隔 |
Enum |
str |
一组字符串 |
PickleType |
任何 Python 对象 |
自动使用 Pickle 序列化 |
LargeBinary |
str |
二进制文件 |
我们在定义数据模型的时候,就需要根据模型的不同字段的意义给字段匹配不同的类型。比如类似于数量的字段,就需要整型,类似于名称,说明或者内容的字段就需要字符型。
对于不同的字段,可能还会有一些选项,比如该字段是否是主键,该字段的值是否允许重复,这些选项也被称为列选项。sqlalchemy的常见列选项见下表。
选项名称 |
选项说明 |
primary_key |
如果设为 True,该列就是表的主键,注意:每个模型都应该有一个主键。一般会专门用一个名为id的字段来定义主键 |
unique |
如果设为 True,该列不允许出现重复的值 |
index |
如果设为 True,为该列创建索引,提升查询效率 |
nullable |
如果设为 True,该列允许使用空值;如果设为 False,这列不允许使用空值 |
default |
为该列定义默认值 |
下面我们就以小王的项目为例,来构建一个数据模型。对于不同品牌的车,我们可以设计如下表这个简单的数据模型。
字段名称 |
字段意义 |
id |
主键 |
name |
品牌的名称 如 长安 宝马 名字不能重复 |
content |
品牌介绍 可以为空 |
按照上面的数据模型,在flask中构建数据模型代码如下。
根据上图,定义数据模型的步骤如下:
- 导入SQLAlchemy,代码第二行
- 第7行和第9行用于配置数据库,你也可以把配置封装到一个类里面,如下图
- 实例化一个数据库对象,代码第十行
- 以db.Model为父类,构建数据模型类,代码13---20行
- 数据模型中的字段均是db.Colum类的实例,实例化传参的时候指定字段类型和选项,见代码15---17行
- 最后,重载了魔法方法__repr__,这个魔法方法只是为了在调试或者测试的时候,让模型对象有更好的可读性,不是必须实现的。
“老项,我记得数据库里面表和表之间还有关系,比如一对多,这种关系在sqlalchemy该如何表达呢?”老项刚讲完数据模型的定义,小王又开始提问了。
“这个就是我马上要说的,就是如何在sqlalchemy中表达不同的数据表关系”老项回答完,又开始继续后面的讲解。
sqlalchemy表关系
在sqlalchemy中,用一些选项来表达表和表之间的一些关系。选项关系如下表所示。
选项名称 |
选项说明 |
backref |
在关系的另一个模型中添加反向引用 |
primaryjoin |
明确指定两个模型之间使用的联结条件。只在模棱两可的关系中需要指定. |
lazy
|
指定如何加载相关记录。可选值如下 : |
select(首次访问时按需加载) |
|
immediate(源对象加载后就加载) |
|
joined(加载记录,但使用联结) |
|
subquery(立即加载,但使用子查询) |
|
noload(永不加载) |
|
dynamic(不加载记录,但提供加载记录的查询) |
|
uselist |
如果设为 Fales,不使用列表,而使用标量值 |
order_by |
指定关系中记录的排序方式 |
secondary |
指定多对多关系中关系表的名字 |
secondaryjoin |
SQLAlchemy 无法自行决定时,指定多对多关系中的二级联结条件 |
还是以小王的项目为例,不同的汽车品牌下,还有不同的车系,比如比亚迪品牌下,还有秦,宋,元等不同车系,所以我们还需要一个车系表。而品牌表和车系表就是一种一对多的关系,一个品牌下可以有多种车系,而某一个车系只能对应一种品牌。我们车系表设计如下。
字段名称 |
字段说明 |
id |
车系id |
name |
车系名称 不可重复 |
content |
车系说明 可以为空 |
status |
车系状态 停售或者在售 |
根据数据模型的设计要求以及外键关系,我们的代码如下图。
注意代码的27行,是backref的固定写法,表示反向引用,这个relationship方法的第一个参数表关联那个模型类,这里关联的是CarSerial类,第二个参数表示新增的属性,名字可以任意取。我们再看代码的39行,该行代码定义了一个外键的id,注意这个id应该在一对多中,多的一方定义,定义的时候第二个参数使用了db.ForeignKey来确定外键关系,里面的参数就是一对多中,一那一方的id。
我们的数据模型建立好以后,就可以对数据库进行操作了。
数据库的orm操作
创建并增加数据
我们暂时把数据库的操作放在index的视图里面。创建表和插入数据的操作请参考如下图代码。
上图代码中,创建表并增加数据的操作步骤如下:
- 使用create_all()方法创建表,第47行代码
- 实例化模型对象,第48行代码,注意这里不需要传递id这个参数
- 使用会话session添加数据行,第49行代码
- 使用会话进行提交处理,第50行。注意提交以后,数据才能生效
- 如果有一对多关系,应该先提交一的一方,提交后,在对多的一方进行操作,包括实例化模型对象,添加数据化和提交等操作。52---55行。
- 也可以使用add_all()方法,同时添加多个数据行,第54行代码。
我们访问http://127.0.0.1:5000/index,然后到数据库工具进行查询,可以看到如下结果。
查询数据
我们创建了表,并添加了一些数据。下面我们查询一下数据,并把数据通过视图方法展示到页面上。前后台代码如下。
在后端代码中,47行使用all()方法查询品牌表中的所有数据,48行使用get方法查询id为1的数据,注意参数是id,49行使用会话的方式配合filter查询id为1这个品牌下的所有车系。50行直接使用对象的query.filter进行过滤查询。注意过滤条件写“==”。然后按照模板讲的方法对数据进行传递,在前端页面渲染数据。就可以在页面*问到数据库的数据了。filter支持下表更多的过滤操作。
操作案例(只写出filter方法里面的内容) |
意义(请运行代码,自行将意义填写在表中) |
CarSerial.name==‘宝马’ |
|
CarSerial.name!=‘宝马’ |
|
CarSerial.name.like(‘bwm’) |
|
CarSerial.name.in_(['奔驰', '宝马']) |
|
~CarSerial.name.in_(['奔驰', '宝马']) |
|
CarSerial.name == None |
|
CarSerial.name != None |
|
from sqlalchemy import and_ and_( CarSerial.name == '宝马', CarSerial.content == '华晨宝马') |
|
from sqlalchemy import or_ or_( CarSerial.name == '宝马', CarSerial.content == '华晨宝马')
|
|
删除数据
在flask中,要删除某一条数据也比较简单,代码见下图。
在代码中,删除一条数据前,首先要查询到需要删除的数据(47行代码),然后使用会话的方式执行删除动作并提交(48-49行代码)。操作后的数据库变化如下图所示。
运行前
运行后
修改数据
如果要修改某一条数据,也需要先查询到该数据,然后进行修改,代码如下图。
代码中,修改的主要操作是第48和49行代码,注意修改后也需要提交。修改后数据库的变化如下图。
运行前
运行后
“好了,小王,关于在flask中使用sqlalchemy操作数据库的基本方法就先给你说这么多。今天有空一定要把讲的操作都好好练习一遍,你也可以开始设计数据库,实现数据模型了。”老项讲完,给小王布置了新的任务。
“遵命,老项,保证完成任务。”小王嘿嘿一笑,回答道。
本章任务:
- 在虚拟环境中,安装教程中的两个包
- 配置安装好mysql数据库
- 对教程中的所有案例和任务进行练习
- 根据小王项目的需求,结合汽车品牌,车系,车型等汽车的实际信息,设计数据库
- 实现对应数据库的数据模型