0x004-数据库使用

概述

  1. 使用pymysql做为数据库驱动
  2. 使用flask-sqlalchemy做为数据库orm对象框架
  3. 使用flask-migrate做为数据库版本控制框架

准备工作

安装数据库相关依赖

1
2
3
pip install pymysql
pip install flask-sqlalchemy
pip install flask-migrate

数据库配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# config.py
class Config(object):
...
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://test:qwe123@192.168.232.132/megablog"
SQLALCHEMY_TRACK_MODIFICATIONS = False

----------
# microapp/__init__.py
...
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
...
db = SQLAlchemy(app)
migrate = Migrate(db,app)

数据库关系

一般来说现在有三种数据库关系

  1. 一对一
  2. 多对一
  3. 多对多

一对一

1
在一对多的关系语句上db.relationship()基础上添加一个uselist=False

一对多

1
2
3
4
5
6
7
8
9
10
11
12
13
数据库中如果是一对多的关系,那么就是在唯一的一方,保存id,在多的一方保存关系

比如,
一片文章只能有一个人来发表,那么post里面就保存user_id 作为外键
一个人可以发表多篇文章,那么user里面就保存关系

class Post(db.Model):
...
user_id = db.Column(db.Integer,db.ForeignKey('user.id'))

class User(UserMixin,db.Model):
...
posts = db.relationship('Post',backref='author',lazy='dynamic')

多对多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
多对多比较复杂,有两种情况,需要新建一个表来进行映射,这里我们采用学生和老师的关系来理解第一种情况,即多对多是两个不同的类
1. 一个学生的类,肯定是对应着多位老师的类
2. 一个老师的类,肯定也是对应着多位学生的类
association = db.Table('association',
# 把学生放在了左边
db.Column('student_id',db.Integer,db.ForeignKey('student.id')),

# 把老师放在了右边
db.Column('teacher_id',db.Integer,db.ForeignKey('teacher.id')),
)

class Student(db.Model):
__tablename__ ='student'
id = db.Column(db.Integer,primary_key=True)
name=db.Column(db.String(64))
# 这个backref应该通常要指回自己,代表反向查询
# 比如我们可以通过student.teachers查询到所有老师
# 又通过某个老师查询到所有学生student.teachers[0].students
teachers = db.relationship('Teacher',secondary=association,backref='students')

class Teacher(db.Model):
__tablename__ ='teacher'
id =db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(120))
students = db.relationship('Student',secondary=association,backref='teachers')


# 操作流程
# 添加两个学生两个老师
>>> s = Student(name="johnw1")
>>> s2 = Student(name="jacob")
>>> t1 = Teacher(name="jack1")
>>> t2 = Teacher(name="jack2")
# 在学生里面添加老师
>>> s.teachers.append(t1)
>>> s.teachers.append(t2)
>>> s2.teachers.append(t1)
>>> s2.teachers.append(t2)
# 提交
>>> db.session.add(s)
>>> db.session.add(t1)
>>> db.session.add(t2)
>>> db.session.add(s2)
>>> db.session.commit()
# 结果
>>> s2.teachers
[<Teacher 3>, <Teacher 4>]
>>> s2.teachers[0].student
[<Student 2>, <Student 3>]
>>> s.teachers
[<Teacher 3>, <Teacher 4>]

# 反向查询,通过学生查到老师,再查到一共有几个学生。
>>> s.teachers[0].students
[<Student 2>, <Student 3>]

# 老师添加学生也是同样的情况
>>> s3 = Student(name="wen")
>>> s4 = Student(name="wen2")
>>> t1.students.append(s3)
>>> t1.students.append(s4)
>>> db.session.commit()

第二种情况,多对多是同一个类。以本程序为例子,用户关注和被关注,都是用到了user这个类而已。
1. 首先新建一个映射的表
follow_table = db.Table("follow_table",
db.Column("follower_id",db.Integer,db.ForeignKey('user.id')),
db.Column("followed_id",db.Integer,db.ForeignKey("user.id")))

在这里我们将关注者放在了左边,被关注者放在了右边

2. 创建关系
class User(UserMixin,db.Model):
followed = db.relationship("User",secondary=follow_table,
# 第一层定义关注者
primaryjoin = (follow_table.c.follower_id == id),
# 第二层定义被关注者
secondaryjoin = (follow_table.c.followed_id == id),
# 反向查询,就可以查询出这个被关注者有多少粉丝
backref = db.backref('followers',lazy='dynamic'),lazy='dynamic'))


通过sql打印,我们可以看到
>>> print(u2.followed)
SELECT user.id AS user_id, user.username AS user_username, user.email AS user_email, user.password_hash AS user_password_hash, user.des AS user_des, user.register_date AS user_register_date
FROM user, follow_table
WHERE follow_table.follower_id = %(param_1)s AND follow_table.followed_id = user.id

最后,上面代码中使用了多次 lazy = ‘dynamic’
这是为了避免一次查询的数据太多,如果只是返回一个数据,那就没必要,如果返回大量的数据,那么性能就会提高很多。

数据库ORM写法

1
2
3
4
5
6
7
8
9
10
11
12
class User(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer,primary_key=True)
username = db.Column(db.String(64),index=True,unique=True)
email = db.Column(db.String(120),index=True,unique=True)
password_hash = db.Column(db.String(128))
des = db.Column(db.Text)
register_date = db.Column(db.DateTime,default=datetime.now)
posts = db.relationship('Post',backref='author',lazy='dynamic')

def __repr__(self):
return "<user:{}>".format(self.username)