数据库的设置

Web应用中普遍使用的是关系模型的数据库,关系型数据库把所有的数据都存储在表中,表用来给应用的实体建模,表的列数是固定的,行数是可变的。它使用结构化的查询语言。关系型数据库的列定义了表中表示的实体的数据属性。比如:商品表里有name、price、number等。 Flask本身不限定数据库的选择,你可以选择SQL或NOSQL的任何一种。也可以选择更方便的SQLALchemy,类似于Django的ORM。SQLALchemy实际上是对数据库的抽象,让开发者不用直接和SQL语句打交道,而是通过Python对象来操作数据库,在舍弃一些性能开销的同时,换来的是开发效率的较大提升。

SQLAlchemy是一个关系型数据库框架,它提供了高层的ORM和底层的原生数据库的操作。flask-sqlalchemy是一个简化了SQLAlchemy操作的flask扩展。

下面使用mysql作为示例进行说明。

创建mysql数据库

1.登录数据库

mysql -u root -p password

2.创建数据库,并设定编码

create database <数据库名> charset=utf8;

3.显示所有数据库

show databases;

4.执行

create database flask_test charset=utf8;

安装flask-sqlalchemy的扩展

pip install -U Flask-SQLAlchemy 

python2:要连接mysql数据库,仍需要安装flask-mysqldb

pip install flask-mysqldb

python3:要连接mysql数据库,仍需要安装pymysql

pip install pymysql

本篇章内容以python3作为开讲。

使用Flask-SQLAlchemy连接mysql数据库

使用Flask-SQLAlchemy扩展操作数据库,首先需要建立数据库连接。数据库连接通过URL指定,而且程序使用的数据库必须保存到Flask配置对象的SQLALCHEMY_DATABASE_URI键中。

对比下Django和Flask中的数据库设置:

Django的数据库设置:

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 修改后端数据库使用mysql
'NAME': 'mydb', # 设置访问数据库名称
'USER': 'root', # 访问访问mysql用户名
'PASSWORD': 'password', # 设置访问密码
'HOST': 'localhost', # 设置访问ip地址
'PORT': 3306, # 设置访问端口号
}

Flask的数据库设置:  

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://user:password@127.0.0.1:3306/database'

常用的SQLAlchemy字段类型

上面看完了如何设置连接数据库,那么来看看,使用SQLAlchemy创建数据模型的时候,基本的字段类型如下:

类型名 python中类型 说明
Integer int 普通整数,一般是32位
SmallInteger int 取值范围小的整数,一般是16位
BigInteger int或long 不限制精度的整数
Float float 浮点数
Numeric decimal.Decimal 普通整数,一般是32位
String str 变长字符串
Text str 变长字符串,对较长或不限长度的字符串做了优化
Unicode unicode 变长Unicode字符串
UnicodeText unicode 变长Unicode字符串,对较长或不限长度的字符串做了优化
Boolean bool 布尔值
Date datetime.date 时间
Time datetime.datetime 日期和时间
LargeBinary str 二进制文件

常用的SQLAlchemy列选项

选项名 说明
primary_key 如果为True,代表表的主键
unique 如果为True,代表这列不允许出现重复的值
index 如果为True,为这列创建索引,提高查询效率
nullable 如果为True,允许有空值,如果为False,不允许有空值
default 为这列定义默认值

常用的SQLAlchemy关系选项

选项名 说明
backref 在关系的另一模型中添加反向引用
primary join 明确指定两个模型之间使用的联结条件
uselist 如果为False,不使用列表,而使用标量值
order_by 指定关系中记录的排序方式
secondary 指定多对多中记录的排序方式
secondary join 在SQLAlchemy中无法自行决定时,指定多对多关系中的二级联结条件

上面这些有很多基本选项的说明,下面来进行数据库的基本增删改等操作来加强理解。

数据库基本操作

在Flask-SQLAlchemy中,插入、修改、删除操作,均由数据库会话管理。会话用db.session表示。在准备把数据写入数据库前,要先将数据添加到会话中然后调用commit()方法提交会话。

数据库会话是为了保证数据的一致性,避免因部分更新导致数据不一致。提交操作把会话对象全部写入数据库,如果写入过程发生错误,整个会话都会失效。

数据库会话也可以回滚,通过db.session.rollback()方法,实现会话提交数据前的状态。

在Flask-SQLAlchemy中,查询操作是通过query对象操作数据。最基本的查询是返回表中所有数据,可以通过过滤器进行更精确的数据库查询。

下面先来创建两个表的数据模型:用户表和角色表。

在视图函数中定义模型类

看完了上面那么多的概念说明,下面来看看如何创建数据模型以及创建数据表,如下:

1.在脚本15_SQLAlchemy.py编写创建User和Role数据模型

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import pymysql
pymysql.install_as_MySQLdb() app = Flask(__name__) class Config(object):
"""配置参数"""
# 设置连接数据库的URL
user = 'root'
password = '********'
database = 'flask_test'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://%s:%s@127.0.0.1:3306/%s' % (user,password,database) # 设置sqlalchemy自动更跟踪数据库
SQLALCHEMY_TRACK_MODIFICATIONS = True # 查询时会显示原始SQL语句
app.config['SQLALCHEMY_ECHO'] = True # 禁止自动提交数据处理
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = False # 读取配置
app.config.from_object(Config) # 创建数据库sqlalchemy工具对象
db = SQLAlchemy(app) class Role(db.Model):
# 定义表名
__tablename__ = 'roles'
# 定义字段
id = db.Column(db.Integer, primary_key=True,autoincrement=True)
name = db.Column(db.String(64), unique=True)
users = db.relationship('User',backref='role') # 反推与role关联的多个User模型对象 class User(db.Model):
# 定义表名
__tablename__ = 'users'
# 定义字段
id = db.Column(db.Integer, primary_key=True,autoincrement=True)
name = db.Column(db.String(64), unique=True, index=True)
email = db.Column(db.String(64),unique=True)
pswd = db.Column(db.String(64))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) # 设置外键 if __name__ == '__main__': # 删除所有表
db.drop_all() # 创建所有表
db.create_all()
  1. 执行脚本,创建数据库
python3 15_SQLAlchemy.py

3.在mysql查看已经创建的表结构

+--------------------+
| Tables_in_flask_ex |
+--------------------+
| roles |
| users |
+--------------------+
2 rows in set (0.00 sec) mysql>
mysql> desc users;
+---------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(64) | YES | UNI | NULL | |
| email | varchar(64) | YES | UNI | NULL | |
| pswd | varchar(64) | YES | | NULL | |
| role_id | int(11) | YES | MUL | NULL | |
+---------+-------------+------+-----+---------+----------------+
5 rows in set (0.00 sec) mysql>
mysql> desc roles;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(64) | YES | UNI | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec) mysql>

创建好了数据表之后,下面来看看如何执行数据的增删查改的。

常用的SQLAlchemy查询过滤器

过滤器 说明
filter() 把过滤器添加到原查询上,返回一个新查询
filter_by() 把等值过滤器添加到原查询上,返回一个新查询
limit 使用指定的值限定原查询返回的结果
offset() 偏移原查询返回的结果,返回一个新查询
order_by() 根据指定条件对原查询结果进行排序,返回一个新查询
group_by() 根据指定条件对原查询结果进行分组,返回一个新查询

常用的SQLAlchemy查询执行器

方法 说明
all() 以列表形式返回查询的所有结果
first() 返回查询的第一个结果,如果未查到,返回None
first_or_404() 返回查询的第一个结果,如果未查到,返回404
get() 返回指定主键对应的行,如不存在,返回None
get_or_404() 返回指定主键对应的行,如不存在,返回404
count() 返回查询结果的数量
paginate() 返回一个Paginate对象,它包含指定范围内的结果

创建表:

db.create_all()

删除表

db.drop_all()

每次插入单条数据

if __name__ == '__main__':

    # 插入一条角色数据
role1 = Role(name='admin')
db.session.add(role1)
db.session.commit() # 再次插入一条数据
role2 = Role(name='user')
db.session.add(role2)
db.session.commit()

执行脚本:

python3 15_SQLAlchemy.py

在mysql中查看插入的数据,如下:

mysql> select * from roles \G
*************************** 1. row ***************************
id: 1
name: admin
*************************** 2. row ***************************
id: 2
name: user
2 rows in set (0.00 sec)

一次插入多条数据

 # 一次性插入多条数据
user1 = User(name='wang',email='wang@163.com',pswd='123456',role_id=role1.id)
user2 = User(name='zhang',email='zhang@189.com',pswd='201512',role_id=role2.id)
user3 = User(name='chen',email='chen@126.com',pswd='987654',role_id=role2.id)
user4 = User(name='zhou',email='zhou@163.com',pswd='456789',role_id=role1.id)
db.session.add_all([user1,user2,user3,user4])
db.session.commit()

执行插入数据,如下:

python3 15_SQLAlchemy.py

在mysql中查询插入的数据如下:

mysql> select * from users \G
*************************** 1. row ***************************
id: 1
name: wang
email: wang@163.com
pswd: 123456
role_id: 1
*************************** 2. row ***************************
id: 2
name: zhang
email: zhang@189.com
pswd: 201512
role_id: 2
*************************** 3. row ***************************
id: 3
name: chen
email: chen@126.com
pswd: 987654
role_id: 2
*************************** 4. row ***************************
id: 4
name: zhou
email: zhou@163.com
pswd: 456789
role_id: 1
4 rows in set (0.00 sec) mysql>

虽然这里在python中看上去是一次性插入多条数据,其实在mysql也是执行多行插入的语句,通过mysql的日志可以看到如下:

2019-11-23T16:48:56.984459Z	 9061 Query	INSERT INTO users (name, email, pswd, role_id) VALUES ('wang', 'wang@163.com', '123456', 1)
2019-11-23T16:48:56.997132Z 9061 Query INSERT INTO users (name, email, pswd, role_id) VALUES ('zhang', 'zhang@189.com', '201512', 2)
2019-11-23T16:48:57.010175Z 9061 Query INSERT INTO users (name, email, pswd, role_id) VALUES ('chen', 'chen@126.com', '987654', 2)
2019-11-23T16:48:57.024134Z 9061 Query INSERT INTO users (name, email, pswd, role_id) VALUES ('zhou', 'zhou@163.com', '456789', 1)

实际上并没有将多个values合并到一个insert语句,依然是多个insert语句逐个插入的。

查询:filter_by精确查询

返回名字等于'wang'的所有user

User.query.filter_by(name='wang').all()

first()返回查询到的第一个对象

User.query.first()

all()返回查询到的所有对象

User.query.all()

filter模糊查询,返回名字结尾字符为g的所有数据。

User.query.filter(User.name.endswith('g')).all()

get(),参数为主键,如果主键不存在没有返回内容

User.query.get(1)

逻辑非,返回名字不等于wang的所有数据。

User.query.filter(User.name!='wang').all()

逻辑与,需要导入and_,返回and_()条件满足的所有数据。

from sqlalchemy import and_
User.query.filter(and_(User.name!='wang',User.email.endswith('163.com'))).all()

逻辑或,需要导入or_

from sqlalchemy import or_
User.query.filter(or_(User.name!='wang',User.email.endswith('163.com'))).all()

not_ 相当于取反

from sqlalchemy import not_
User.query.filter(not_(User.name=='chen')).all()

查询数据后删除

user = User.query.first()
db.session.delete(user)
db.session.commit()
User.query.all()

更新数据

user = User.query.first()
user.name = 'dong'
db.session.commit()
User.query.first()

使用update

User.query.filter_by(name='zhang').update({'name':'li'})

关联查询示例:角色和用户的关系是一对多的关系,一个角色可以有多个用户,一个用户只能属于一个角色。

关联查询角色的所有用户:

#查询roles表id为1的角色
role1 = Role.query.get(1)
#查询该角色的所有用户
role1.users

关联查询用户所属角色:

#查询users表id为3的用户
user1 = User.query.get(3)
#查询用户属于什么角色
user1.role

  

参考链接:
https://www.jianshu.com/p/7e32074e4fad
https://juejin.cn/post/6844904030892900366
 

Flask 操作Mysql数据库 - flask-sqlalchemy扩展的更多相关文章

  1. Python 操作mysql数据库之 SQLAlchemy 案例详解

      前言: 字段声明类型中,最右边的是数据库中对应的字段,我们依然可以使用,其左边的的 SQLAchemy 则是其自身封装的自定义类型. 本篇不会讲太多的理论知识,因为这个实用性更强,所以通篇全部都是 ...

  2. flask 操作mysql的两种方式-sqlalchemy操作

    flask 操作mysql的两种方式-sqlalchemy操作 二.ORM sqlalchemy操作 #coding=utf-8 # model.py from app import db class ...

  3. php笔记08:数据库编程---使用php的MySQL扩展库操作MySQL数据库

    1.使用php的MySQL扩展库操作MySQL数据库: php有3种方式操作MySQL数据库 (1)mysql扩展库 (2)mysqli扩展库 (3)pdo     mysql扩展库与mysql数据库 ...

  4. mysqli扩展库操作mysql数据库

    配置环境 配置php.ini文件让php支持mysqli扩展库 extension=php_mysqli.dll 建库建表 详见博客 “mysql扩展库操作mysql数据库” 查询数据库 <?p ...

  5. python【第十二篇下】操作MySQL数据库以及ORM之 sqlalchemy

    内容一览: 1.Python操作MySQL数据库 2.ORM sqlalchemy学习 1.Python操作MySQL数据库 2. ORM sqlachemy 2.1 ORM简介 对象关系映射(英语: ...

  6. flask 操作mysql的两种方式-sql操作

    flask 操作mysql的两种方式-sql操作 一.用常规的sql语句操作 # coding=utf-8 # model.py import MySQLdb def get_conn(): conn ...

  7. 使用php的mysqli扩展库操作mysql数据库

    简单介绍mysqli: 1.mysqli(mysql improve mysql扩展库的增强版) mysql扩展库和mysqli扩展库的区别 1.mysqli的稳定性  安全性 和 执行效率有所提高 ...

  8. PHP mysql 扩展库 操作mysql数据库步骤

    一.mysql 扩展库操作mysql数据库步骤如下: 1.获取连接 2.选择数据库 3.设置操作编码 4.发送指令(ddl数据定义/dml数据操作/dql数据查询/dtl数据事务控制) 5.接收返回的 ...

  9. 【tips】ORM - SQLAlchemy操作MySQL数据库

    优先(官方文档SQLAlchemy-version1.2): sqlalchemy | 作者:斯芬克斯 推荐一(长篇幅version1.2.0b3):python约会之ORM-sqlalchemy | ...

随机推荐

  1. Python使用import导入模块时执行了模块的文件但报ModuleNotFoundError错误的愚蠢问题

    老猿在学习import导入自定义模块时,搜索路径中sys.path中已经添加对应路径,发现会报ModuleNotFoundError,但对应的模块代码被执行了,代码myfib.py如下: def fi ...

  2. MySQL(14)---Docker搭建MySQL主从复制(一主一从)

    Docker搭建MySQL主从复制(一主一从) 上一篇博客写了MYSQL主从复制原理 : MySQL(13)---MYSQL主从复制原理 这篇我们来写 Docker搭建MYSQL主从复制(一主一从) ...

  3. Java获取不到请求的真实IP

    问题 最近在写博客浏览量的时候,设计了这么一个逻辑:同一个IP浏览一遍文章,5分钟内不刷新次数.就需要在服务器端得到用户的真实IP,我代码是这样写的(从网上找的方法): public static S ...

  4. 对 精致码农大佬 说的 Task.Run 会存在 内存泄漏 的思考

    一:背景 1. 讲故事 这段时间项目延期,加班比较厉害,博客就稍微停了停,不过还是得持续的技术输出呀! 园子里最近挺热闹的,精致码农大佬分享了三篇文章: 为什么要小心使用 Task.Run [http ...

  5. 半夜删你代码队 Day2冲刺

    一.每日站立式会议 1.站立式会议 成员 昨日完成工作 今日计划工作 遇到的困难 陈惠霖 整理任务 了解相关网页设计 任务安排有的不合理,需改进 侯晓龙 学习了解相关知识 尝试写第一个实例子 无 周楚 ...

  6. Alpha冲刺——序言篇(任务与计划)

    Alpha冲刺--序言篇(任务与计划) 1.整个项目预期的任务量 需求规格说明书 架构设计,原型设计,原型改进(给目标用户展现原型,并进一步理解需求) 编码规范完成.平台环境搭建完成.初步架构搭建 队 ...

  7. 链判断运算符和Null 判断运算符

    链判断运算符 如果我们要获取一个对象的深层嵌套属性,例如获取文章标题res.data.article.title,然后为了安全起见,我们肯定不能直接这样获取,万一res对象没有article属性了呢, ...

  8. Linux 开机启动程序的顺序

    1.加载BISO的硬件信息,并取得第一个开机代号 2.读取第一个开机装置的mbr的boot loader的信息 3.加载kernel操作系统核心信息,开始解压缩,并驱动所有硬件装置 4.kernel执 ...

  9. PluginOK中间件高级版-支持在Chrome、Edge、Firefox等浏览器网页中真正内嵌ActiveX等控件运行的版本已获多家上市公司采购

    PluginOK(牛插)中间件(原名:本网通WebRunLocal)是一个实现WEB浏览器(Web Browser)与本地程序(Local Application)之间进行双向调用的低成本.强兼容.安 ...

  10. 【面试专栏】Java并发编程:volatile关键字

    1.  内存模型 若一个变量在多线程环境下同时操作,则可能出现结果不一致的情况.这就是常说的缓存不一致性问题. 解决缓存不一致问题,通常有两个解决方案: 通过在总线加LOCK#锁的方式 因为CPU和其 ...