【自动化运维新手村】Flask-ORM

摘要

在上一章节【删库跑路第一步】中,我们已经了解了基本的数据库的概念,以及常用的数据表操作,并且我希望大家意识到,如果在后端应用不必须依赖数据库时,最好不要引入;但如果引入,那么就最好在使用之前了解一些数据库的基础知识,比如:从手动写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的操作,实现数据表的增删改查。

代码整理如下:

# /models.py

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__':
    # 增
    # 删
    # 改
    # 查
# /models.py
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字段。

批量新增如下:

# /models.py
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框架实现对数据库的操作,所以希望大家务必要亲自实践操作,否则后面的学习内容就无法顺利地开展。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值