摘要
在上一章节【删库跑路第一步】中,我们已经了解了基本的数据库的概念,以及常用的数据表操作,并且我希望大家意识到,如果在后端应用不必须依赖数据库时,最好不要引入;但如果引入,那么就最好在使用之前了解一些数据库的基础知识,比如:从手动写SQL语句建数据表开始。
在建好表后,今天这一章节,就开始讲解如何在后端应用中集成对数据库操作的能力。
ORM
全称叫做对象关系映射(Object Relational Mapping,简称ORM)
ORM的名称上就已经十分清晰的表明其功能和用途:
Object:对象,这里是指编程语言中的对象,例如Python/Java中的Class,或者Golang中的struct
Relational:关系,表示特指关系型数据库,如MySQL,Oracle,PostgreSQL
Mapping:映射,指将编程语言的对象和关系型数据库之间进行相互关联,例如对象名表示数据表名,对象属性表示数据表列名,以及数据类型等等。
映射关系如图所示:
正是由于ORM具有的以上特点,所以在继承了ORM框架的后端应用,如Django,Flask中,才可以实现定义好Model类(对象模型)后,可以一键将其在数据库中创建出对应的数据表结构。
Flask-SQLAlchemy框架
安装
Flask作为微框架,其集成的几乎所有的能力都来自于插件,ORM框架就是提供数据库操作能力的一种插件。
SQLAlchemy,就是一个Python中十分常用的ORM框架,它提供了高层的ORM和底层的原生数据库的操作,让开发者不用直接和 SQL 语句打交道,而是通过 Python 对象来操作数据库。
而Flask-SQLAlchemy 是Flask应用中的扩展,它旨在通过提供有用的默认值和额外的帮助程序来简化SQLAlchemy在Flask应用中的使用,从而更轻松地完成常见任务。
执行以下命令安装扩展包
pip install pymysql
pip install flask-sqlalchemy
如果安装速度较慢,可以尝试指定镜像源地址
pip install flask-sqlalchemy -i https://pypi.tuna.tsinghua.edu.cn/simple
引入
将Flask-SQLAlchemy引入到Flask应用中十分简单,如下:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://root:root@127.0.0.1:3306/ops?charset=utf8"
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
使用Flask-SQLAlchemy扩展到简易性显而易见,只需要设置一个SQLALCHEMY_DATABASE_URI ,该变量的格式如下:
mysql+pymysql://username:password@host:port/db_name?charset=utf8
大家根据自己数据库的设置进行替换即可。
除了数据库URI的设置,我这里还十分推荐大家设置一个变量,那就是SQLALCHEMY_ECHO ,该变量为True 时,可以打印出ORM框架操作对应的数据库SQL语句,一方面对于刚接触数据库的朋友来说,可以更熟悉ORM框架与数据库之间的映射,另一方面也利于排查问题。
Model
在引入db 之后,就可以开始定义Model模型了,根据上一章节中的数据表结构,我们可以对应的定义出对象模型,如下:
class Devices(db.Model):
__tablename__ = 'devices'
id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment="自增主键")
ip = db.Column(db.String(16), nullable=False, comment="IP地址")
hostname = db.Column(db.String(128), nullable=False, comment="主机名")
idc = db.Column(db.String(32), comment="机房")
row = db.Column(db.String(8), comment="机柜行")
column = db.Column(db.String(8), comment="机柜列")
vendor = db.Column(db.String(16), comment="厂商")
model = db.Column(db.String(16), comment="型号")
role = db.Column(db.String(8), comment="角色")
created_at = db.Column(db.DateTime(), nullable=False, server_default=text('NOW()'), comment="创建时间")
updated_at = db.Column(db.DateTime(), nullable=False, server_default=text('NOW()'), server_onupdate=text('NOW()'), comment="修改时间")
这里与上节相比新增了两列,分别是created_at, updated_at ,在新增一行数据或者某一行数据修改时,会更新数据创建或修改的时间。
大家可以参照数据表的SQL语句来对比一下,ORM框架中模型与数据库的映射,
CREATE TABLE IF NOT EXISTS `devices` (
`id` INT AUTO_INCREMENT COMMENT '自增主键',
`ip` VARCHAR(16) NOT NULL COMMENT 'IP地址',
`hostname` VARCHAR(128) COMMENT '主机名',
`idc` VARCHAR(32) COMMENT '机房',
`row` VARCHAR(8) COMMENT '机柜行',
`column` VARCHAR(8) COMMENT '机柜列',
`vendor` VARCHAR(16) COMMENT '厂商',
`model` VARCHAR(16) COMMENT '型号',
`role` VARCHAR(8) COMMENT '角色',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
数据类型及列选项
从上面的Model创建和SQL语句的对比来看,Flask-SQLAlchemy与数据库之间的数据类型存在特定的映射关系,由于Flask-SQLAlchemy是基于SQLAlchemy实现的,所以其数据类型与SQLAlchemy相同。
SQLAlchemy中常用数据类型:
SQLAlchemy类型 | 说明 |
---|
Integer | 整形,映射到数据库中是int类型 | Float | 浮点类型,映射到数据库中是float类型。他占据的32位 | Double | 双精度浮点类型,映射到数据库中是double类型,占据64位 | String | 可变字符类型,映射到数据库中是varchar类型. | Boolean | 布尔类型,映射到数据库中的是tinyint类型 | Date | 存储时间,只能存储年月日。映射到数据库中是date类型 | DateTime | 存储时间,可以存储年月日时分秒毫秒等。映射到数据库中也是datetime类型 | Timestamp | 存储时间,可以存储时分秒。映射到数据库中也是timestamp类型 | Text | 存储长字符串。一般可以存储6W多个字符,映射到数据库中就是text类型 | LongText | 长文本类型,映射到数据库中是longtext类型 |
除了常用的数据类型之外,我们SQL语句中还在定义列的时候,指定了很多属性,这些在SQLAlchemy中也有同样的定义。
SQLAlchemy中常用的列选项
SQLAlchemy列属性 | 说明 |
---|
primary_key | 如果设为True,这列就是表的主键 | unique | 如果设为True,这列不允许出现重复的值 | index | 如果设为True,这列创建索引,提升查询效率 | nullable | 如果设为True,这列允许使用空值;如果设为False,这列不允许使用空值 | comment | 列的描述 | autoincrement | 列为int类型主键时,设置后可以自增 | server_default | 设置在远端数据库的默认值 | server_onupdate | 某一列更新时,设置在远端数据库的默认值 |
增删改查
在定义好Model之后,就进入了最为重要的一步,那就是通过对Model的操作,实现数据表的增删改查。
代码整理如下:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://root:YfyH98333498.@127.0.0.1:3306/ops?charset=utf8"
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
class Devices(db.Model):
__tablename__ = 'devices'
id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment="自增主键")
ip = db.Column(db.String(16), nullable=False, comment="IP地址")
hostname = db.Column(db.String(128), nullable=False, comment="主机名")
idc = db.Column(db.String(32), comment="机房")
row = db.Column(db.String(8), comment="机柜行")
column = db.Column(db.String(8), comment="机柜列")
vendor = db.Column(db.String(16), comment="厂商")
model = db.Column(db.String(16), comment="型号")
role = db.Column(db.String(8), comment="角色")
created_at = db.Column(db.DateTime(), nullable=False, server_default=text('NOW()'), comment="创建时间")
updated_at = db.Column(db.DateTime(), nullable=False, server_default=text('NOW()'), server_onupdate=text('NOW()'), comment="修改时间")
if __name__ == '__main__':
增
if __name__ == '__main__':
device = Devices(ip="10.0.0.1", hostname="BJ-R01-C01-N9K-00-00-01", idc="Beijing", row="R01", column="C01", vendor="Cisco", model="Nexus9000", role="CSW")
db.session.add(device)
db.session.commit()
这里在操作数据时会通过db.session 来进行操作,并且执行完语句之后需要执行db.session.commit() 来提交操作,这是由于Flask-SQLAlchemy通过session机制保证了在多线程操作数据库时互不影响,具体的原理我们会单独在番外篇中提到。
执行python models.py 后可以看到控制台输出如下:
2022-03-06 20:03:35,775 INFO sqlalchemy.engine.Engine SELECT @@sql_mode
2022-03-06 20:03:35,776 INFO sqlalchemy.engine.Engine SELECT @@lower_case_table_names
2022-03-06 20:03:35,777 INFO sqlalchemy.engine.Engine SELECT DATABASE()
2022-03-06 20:03:35,778 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2022-03-06 20:03:35,780 INFO sqlalchemy.engine.Engine INSERT INTO devices (ip, hostname, idc, `row`, `column`, vendor, model, `role`) VALUES (%(ip)s, %(hostname)s, %(idc)s, %(row)s, %(column)s, %(vendor)s, %(model)s, %(role)s)
2022-03-06 20:03:35,781 INFO sqlalchemy.engine.Engine [generated in 0.00023s] {'ip': '10.0.0.1', 'hostname': 'BJ-R01-C01-N9K-00-00-01', 'idc': 'Beijing', 'row': 'R01', 'column': 'C01', 'vendor': 'Cisco', 'model': 'Nexus9000', 'role': 'CSW'}
2022-03-06 20:03:35,783 INFO sqlalchemy.engine.Engine COMMIT
执行select * from devices\G; 查询数据库结果如下:
mysql> select * from devices\G;
*************************** 1. row ***************************
id: 1
ip: 10.0.0.1
hostname: BJ-R01-C01-N9K-00-00-01
idc: Beijing
row: R01
column: C01
vendor: Cisco
model: Nexus9000
role: CSW
created_at: 2022-03-06 20:03:35
updated_at: 2022-03-06 20:03:35
1 row in set (0.00 sec)
可以看到已经成功插入了一条数据,并且数据库自动插入了id, created_at, updated_at 字段。
批量新增如下:
if __name__ == '__main__':
device1 = Devices(ip="10.0.0.1", hostname="BJ-R01-C01-N9K-00-00-01", idc="Beijing", row="R01", column="C01", vendor="Cisco", model="Nexus9000", role="CSW")
device2 = Devices(ip="10.0.0.2", hostname="BJ-R01-C01-N9K-00-00-02", idc="Beijing", row="R01", column="C02", vendor="Cisco", model="Nexus9000", role="CSW")
device3 = Devices(ip="10.0.0.3", hostname="BJ-R01-C01-N9K-00-00-03", idc="Beijing", row="R01", column="C03", vendor="Cisco", model="Nexus9000", role="CSW")
db.session.add_all(device1, device2, device3)
db.session.commit()
查询数据库结果如下:
mysql> select * from devices\G;
*************************** 1. row ***************************
id: 5
ip: 10.0.0.1
hostname: BJ-R01-C01-N9K-00-00-01
idc: Beijing
row: R01
column: C01
vendor: Cisco
model: Nexus9000
role: CSW
created_at: 2022-03-06 20:18:52
updated_at: 2022-03-06 20:18:52
*************************** 2. row ***************************
id: 6
ip: 10.0.0.2
hostname: BJ-R01-C01-N9K-00-00-02
idc: Beijing
row: R01
column: C02
vendor: Cisco
model: Nexus9000
role: CSW
created_at: 2022-03-06 20:18:52
updated_at: 2022-03-06 20:18:52
*************************** 3. row ***************************
id: 7
ip: 10.0.0.3
hostname: BJ-R01-C01-N9K-00-00-03
idc: Beijing
row: R01
column: C03
vendor: Cisco
model: Nexus9000
role: CSW
created_at: 2022-03-06 20:18:52
updated_at: 2022-03-06 20:18:52
3 rows in set (0.00 sec)
删、改、查
删除和修改都需要基于查询,由于查询的篇幅较多,我们都统一放到下一章节再讲解。
总结
这一章节是比较重要的一节,我们首次在后端应用中引入数据库,并通过ORM框架实现对数据库的操作,所以希望大家务必要亲自实践操作,否则后面的学习内容就无法顺利地开展。
|