Flask MySQL 数据库开发

730 阅读8分钟

MySQL

MySQL 社区版是开源免费的,性能也不错的,所以这里选择了MySQL 。

安装 MySQL

MySQL 官网,进入download 模块,下载社区版,

这里安装的是MySQL5.6 版本,勾选,Next,

选择自定义安装,

可以根据自己的的情况选择,这里选择MySQL server 5.6.26 - X64 ,MySQL Workbench 6.3.4 - X64 是图形化的客户端工具,Next,

在这个界面系统会让你确认这些组件是不是你所要安装的组件,点击Execute ,执行安装完成,Next,

可以看到MySQL server 5.6.26 需要进行一个配置操作,

配置模式,这里选默认开发者机器,使用默认的端口号3306 ,Next,

设置密码,Next,

可以看到,MySQL 在Windows 上的服务名是MySQL56 ,没有必要修改,Next,

点击Execute ,之后点击Finish ,MySQL 的配置工作完成了,Next,

不勾选,就是安装完不打开图形化客户端(之后会讲解登录到MySQL 的方法),Finish 完成了安装。

登录 MySQL

这里介绍一下使用命令窗口登录MySQL ;

登录到MySQL ,首先要MySQL 服务启动才行,如果没启动那自然不行,这里说一下查看MySQL 状况的方法,

Windows 可以到这里,控制面板\所有控制面板项\管理工具,选择服务

在服务可以看到状态,如果没启动,可以右键点击MySQL 服务选择启动就可以,

MySQL 服务启动了,就可以在开始菜单选择MySQL 5.6 Command Line Client 来打开命令窗口,

打开之后输入密码,

如图登录成功,

输入exit 可以退出;

还可以使用第三方的客户端工具,Navicat 挺好用的,可以自行安装使用;

这里使用了一个叫MySQL-Front 的工具,

安装,会先让你创建一个连接,然后使用连接上填写信息去连接到MySQL 数据库,

简单说一下,名称指的是这个连接的名称,Host 是本机的话,就写127.0.0.1 ,填好密码,数据库那一栏可以不填,点击确定,完成连接的创建,

然后打开连接,界面如下,

使用 SQLAlchemy

SQLAlchemy提供了ORM,也提供了使用数据库原生SQL的功能,所以通过它,你可以用面向对象的思想处理数据库操作。

如果你需要快速的开始使用 SQLAlchemy ,那么推荐你使用Flask-SQLAlchemy 扩展。这里可以参考Flask 文档上相关部分

使用命令安装Flask-SQLAlchemy 扩展,

pip3 install flask-sqlalchemy

使用时在Python 导入用到的模块,例如,

from flask_sqlalchemy import SQLAlchemy

用到了Flask-MySQLdb 模块,安装如下,

pip install flask-mysqldb

配置数据库

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://username:password@127.0.0.1:3306/books_db'

可以看到,地址是由,用户名,密码,主机地址,端口与数据库名字组成的。

SQLALCHEMY_TRACK_MODIFICATIONS ,设置成True(默认情况),Flask-SQLAlchemy 将会追踪对象的修改并且发送信号。

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

这里感觉没必要,关闭了它。

引入SQLAlchemy模块,创建对象,

from flask_sqlalchemy import SQLAlchemy
 
app = Flask(__name__)
db = SQLAlchemy(app)

创建数据库

这里我们可以手动去命令行创建数据库;

mysql> create database books_db;
Query OK, 1 row affected (0.03 sec)

这样在MySQL-Front可以看到books_db 数据库,

也可以在MySQL-Front 右键,新建,数据库;

创建模型

这里的模型就是对应于数据库的表,一个模型对应一张表,之前提到SQLAlchemy 提供了ORM ,这样就不用写SQL 语句就可以完成表的创建;

例如我们要创建这样的两张表,

roles (表名)
id (主键)     name
1              admin (管理员)
2              user  (用户)

users (表名)
id (主键)     name        role_id (外键)
1               小明          1
2               小哈          2
3               小红          2

roles 指明角色,管理员的意味着权限高;users 是用户表,该表role_id 觉定着用户的角色,是外键,对应于roles 的id 。

我们在Python 文件里定义两个类,Role 对应于roles 表,User 对应于users 表,都继承db.Model ,

from sqlalchemy import Column, Integer, String, ForeignKey


class Role(db.Model):
    __tablename__ = 'roles'
    id = Column(Integer, primary_key=True)
    name = Column(String(16), unique=True)


class User(db.Model):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(16), unique=True)
    role_id = Column(Integer,ForeignKey('roles.id'))

可以看到使用了一个名为ForeignKey的构造,其含义为,其所在的列的值域应当被限制在另一个表的指定列的取值范围之类,也就是外键,这样模型就创建完了。

有了模型接下来就可以创建表了,创表语句如下,

db.create_all()

如果要保证每次运行都有一个干净的表,可以先删除表,

if __name__ == '__main__':
    db.drop_all()
    db.create_all()

    app.run(debug=True)

运行一下,在命令行查看,

mysql> use books_db
Database changed
mysql> show tables;
+--------------------+
| Tables_in_books_db |
+--------------------+
| roles              |
| users              |
+--------------------+
2 rows in set (0.00 sec)

mysql> desc roles;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(16) | YES  | UNI | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)

mysql> desc users;
+---------+-------------+------+-----+---------+----------------+
| Field   | Type        | Null | Key | Default | Extra          |
+---------+-------------+------+-----+---------+----------------+
| id      | int(11)     | NO   | PRI | NULL    | auto_increment |
| name    | varchar(16) | YES  | UNI | NULL    |                |
| role_id | int(11)     | YES  | MUL | NULL    |                |
+---------+-------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)

当然也可以在MySQL-Front 查看,

接下来,学习一下,增删改查,

role = Role(name='admin')
db.session.add(role)
db.session.commit()
user = User(name='小明', role_id=role.id)
db.session.add(user)
db.session.commit()

语句执行后,可以在命令行查看,

mysql> select * from roles;
+----+-------+
| id | name  |
+----+-------+
|  1 | admin |
+----+-------+
1 row in set (0.00 sec)

mysql> select * from users;
+----+--------+---------+
| id | name   | role_id |
+----+--------+---------+
|  1 | 小明   |       1 |
+----+--------+---------+
1 row in set (0.00 sec)

db.session.delete(user)
db.session.commit()

user 是模型对象,对应于表里的一条数据。

这里改的话也比较方便的,如下,

role.name='user'
db.session.commit()

查询看一下表里的值已改了,

+----+------+
| id | name |
+----+------+
|  1 | user |
+----+------+

查询操作还是挺多的,这里就简单介绍几种,例如,

  1. 查询所有用户数据
User.query.all()
  1. 查询有多少个用户
User.query.count()
  1. 查询第一个用户
User.query.first()
  1. 查询某个字段为某个值的数据,

如查询id 为1 的用户,

如果要查的字段是主键可以如下,

User.query.get(1)

过滤器的用法,

若查询名字为小明的用户可以如下,

User.query.filter_by(name='小明').first()

也可以如下,

User.query.filter(User.name=='小明').first()

Demo

Demo 地址

这里简单说一下几个点,

加密配置,

app.secret_key='books'

这里配置密钥为books 。

一个查询方式,

from sqlalchemy import exists


is_exist = db.session.query(exists().where(Author.name == author)).scalar()

直接返回bool 型,根据条件Author.name == author 去表查找,找到返回True 。

还用到两个比较重要的知识点,就是外键与关联,接下来重点说一下。

先看一下Demo 里的模型,

class Role(db.Model):
    __tablename__ = 'roles'
    id = Column(Integer, primary_key=True)
    name = Column(String(16), unique=True)

    users = relationship('User', backref='role')

    # 帮助打印
    def __repr__(self):
        return '<Role: %s %s>' % (self.name,self.id)


class User(db.Model):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(16), unique=True)
    role_id = Column(Integer, ForeignKey('roles.id'))

    def __repr__(self):
        return '<User: %s %s %s>' % (self.name,self.id,self.role_id)

可见于之前不同的就是,增加了relationship ,这就是关联属性的,当然使用它也要导入,

from sqlalchemy.orm import relationship

ForeignKey

外键保证数据的完整性,保证数据的一致性

完整性:从表插入数据时会检查外键所在字段在主表里是否存在;

例如,我们这么写没问题,

user1 = User(name='小明', role_id=role1.id)

是因为role1.id 在roles 表里肯定存在, 如果你随便赋值role_id=12 ,且那个12 在roles 表里没有id 为12 的数据,那么就会报错, 可见User 表设置了ForeignKey('roles.id'),则外键role_id 不能随便赋值,且必须为roles 表里存在的id 。

一致性:删除更新主表的字段,则从表会有关联动作。

已删除为例,我们做如下操作,

role1 = Role(name='admin')
role2 = Role(name='user')
db.session.add_all([role1, role2])
db.session.commit()
user1 = User(name='小明', role_id=role1.id)
user2 = User(name='小红', role_id=role2.id)
user3 = User(name='小哈', role_id=role2.id)
db.session.add_all([user1, user2, user3])
db.session.commit()

可见。小明的role_id 为role1.id ,那如果我们只将role1 删除,会有什么现象呢?我们没有对操作,user1 对应的那条数据会怎样呢?

我们来试一下,

db.session.delete(role1)
db.session.commit()

查看数据表,

mysql> select * from roles;
+----+------+
| id | name |
+----+------+
|  2 | user |
+----+------+
1 row in set (0.00 sec)

mysql> select * from users;
+----+--------+---------+
| id | name   | role_id |
+----+--------+---------+
|  1 | 小明   |    NULL |
|  2 | 小红   |       2 |
|  3 | 小哈   |       2 |
+----+--------+---------+
3 rows in set (0.00 sec)

可见roles 表,只有id 为2 的user数据了,因为我们把admin 的数据删除了,

还有,users 表第一条name 为小明的数据的role_id 的值为NULL 了,

可以外键的一致性使users 表里不会出现role_id 的值在roles 表里不存在的那种不一致的现象。

relationship

在数据结构上外键对连表查询并没有太多的帮助,但在sqlalchemy的模型下外键对连表查询有一定的优化,那就是relationship字段,其配合外键一起使用。

没有用relationship 想要查询user1 的角色,分为两步,

  1. 先查出user1 的role_id ;
  2. 在roles 表根据id 查出角色值;

有了relationship之后就不用分为两步走了,只需要一步就能搞定。在定义表的模型时,relationship将roles 与users 表关联在一起,看一下Role 模型这句,

users = relationship('User', backref='roles')

roles 表与users 表关联,backref是反向关联(反向引用),在关系的另一个模型里添加反向引用,

就是这里我们可以这样去理解,

users 在Role 类里,所以是该类的一个属性(不是表的字段),

roles 是反向引用(backref='roles'),所以是User 类的一个属性(不是表的字段),

接下来说一下其使用。

正向查询

print(User.query.get(1))
print(User.query.get(1).roles.name)

输出如下,

<User: 小明 1 1>
admin

roles是主表,users 是从表,通过User1.roles 查到外键关联的roles 表的数据,查users 表返回roles 表的数据,称之为,正向查询。

反向查询

print(Role.query.get(2))
print(Role.query.get(2).users)
print(Role.query.get(2).users[0])
print(Role.query.get(2).users[0].name)

输出如下,

<Role: user 2>
[<User: 小红 2 2>, <User: 小哈 3 2>]
<User: 小红 2 2>
小红

roles是主表,users 是从表,通过role2.users 查到外键关联的users 表的数据,查roles 表返回users 表的数据,称之为,反向查询。