更新于 

Flask DateBase

数据库配置

1
2
3
4
5
6
7
8
9
10
11
12
# 主机名
HOSTNAME = "127.0.0.1"
# MySQL端口
PORT = 3306
# 用户名
USERNAME = 'root'
# 密码
PASSWORD = 'root123'
# 数据库名称
DATABASE = 'books'
# 数据库路由
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"

准备数据库对象

SQLAlchemy

Flask框架支持使用SQLAlchemy进行数据库操作

直接根据app.config创建出db对象
1
db = SQLAlchemy(app) 
先创建空数据库对象,再注入app进行数据库配置
1
2
db = SQLAlchemy()
db.init_app(app)

进行数据库连接测试:

1
2
3
4
with app.app_context(): #拉取app上下文
with db.engine.connect() as conn:
# 操作数据库执行查询语句:select 1
rs = conn.execute(text('select 1'))
migrate
SQLAlchemy浅更新
1
2
with app.app_context():
db.create_all()

SQLAlchemy会将所有继承了 db.ModelORM模型 映射成数据库表,
但这种自动化的映射仅限于表的首次创建(浅更新)

如果修改已存在表内部的结构,SQLAlchemy不会对已存表的数据库结构进行更新,
此处根据ORM模型进行数据库迁移更新的操作可以使用 flask_migrate 来实现

migrate的配置
1
2
from flask_migrate import Migrate
migrate = Migrate(app, db)
Migrate ORM映射成表的三步
1
2
3
4
5
6
7
8
1. 初始化数据库,仅执行一次
flask db init

2. 识别ORM模型的改变,生成迁移脚本(migrationn/versions下的迁移脚本)
flask db migrate

3. 运行迁移脚本
flask db upgrade

在执行完第1步,初始化数据库之后,会在项目根目录下新创建一个migrations文件,文件结构如下:

  • versions
    • 存放每次更新的数据库对应的更新命令(以版本号命名),支持根据数据库版本号进行回退操作
  • alembic.ini : A generic, single database configuration.
  • env.py : Alembic Config object
  • README
  • script.py.mako

创建ORM模型

ORM
  • O Object 对象
  • R Relationship 关系
  • M Map 映射

ORM模型需要:

  • 继承自db.Model
  • 拥有表名属性 tablename
Book
1
2
3
4
5
6
7
8
9
10
class Book(db.Model): 
__tablename__ = 'book'
# 整型 主键 自增
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
# 200长度字符串 非空
name = db.Column(db.String(200), nullable=False)
# 整型 被引用外键author.id
author_id = db.Column(db.Integer, db.ForeignKey("author.id"))
# 在Author表中反向聚集book序列
author = db.relationship("Author", backref="book")
Author
1
2
3
4
5
6
class Authro(db.Model):
__tablename__ = "author"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(200), nullable=False)
country = db.Column(db.String(200), nullable=False)
birthday = db.Column(db.Date)
ForeignKey && relationship
1
db.ForeignKey("target_table.key_name")

通过提供指定表的主键值,能够将对应的整条数据拉取过来,
相当于摇人时手机里的电话号码,比如希望John过来,只需要John的手机号码,
但是把John叫过来之后,需要给John流出一个座位安顿,
对应的,我们也需要为被拉取的数据提供一个存放的位置(甚至可以是其他表!)

1
target_value = db.relationship("target_table_Model", back_populates="back_target_table")
  • 第一个参数代表目标表的ORM模型名称(字符串)
  • back_populates 定义反向引用,用于建立双向关系

现在Book和Author是多对一的关系
Book中拉取对应author的信息,(这本书的作者是X君)
同时也需要在author中收集Books的信息,(X君写了A、B、C等书)
因此为了建立起多对一的关系,需要同时对两张表进行配置:

BookModel
1
2
3
4
class Book(db.Model):
....
author_id = db.Column(db.Integer, db.ForeignKey("author.id"))
author = db.relationship("Author", back_populates="book")
AuthorModel
1
2
3
class Author(db.Model):
...
book = db.relationship("Book", back_populates="author")

为了简化这种双向配置的操作,也可以直接使用backref进行反向聚集配置:

BookModel
1
2
3
4
class Book(db.Model):
...
author_id = db.Column(db.Integer,db.ForeignKey("author.id"))
author = db.relationship("Author", backref="books")
常用的SQLAlchemy关系函数参数
常用的SQLAlchemy关系函数参数
数据库关系配置
WriterModel:一
1
2
3
4
5
class Writer(db.Model):
__tablename__ = 'writer'
id = db.Column(db.Integer, primery_key=True, autoincrement=True)
name = db.Column(db.String(200), nullable=False)
books = db.relationship('Book', backref="writer")
BookModel:多
1
2
3
4
5
class Book(db.Model):
__tablename__ = 'book'
id = db.Column(db.Integer, primery_key=True, autoincrement=True)
name = db.Column(db.String(200), nullable=False)
writer_id = db.Column(db.Integer, db.ForeignKey("writer.id"))
CitizenModel:多
1
2
3
4
5
6
class Citizen(db.Model):
__tablename__ = 'citizen'
id = db.Column(db.Integer, primery_key=True, autoincrement=True)
name = db.Column(db.String(200), nullable=False)
city_id = db.Column(db.Integer, db.ForeignKey("city.id"))
city = db.relationship('City')
City:一
1
2
3
4
class City(db.Model):
__tablename__ = 'city'
id = db.Column(db.Integer, primery_key=True, autoincrement=True)
name = db.Column(db.String(200), nullable=False, unique=True)
Country:一
1
2
3
4
5
class Country(db.Model):
__tablename__ = 'country'
id = db.Column(db.Integer, primery_key=True, autoincrement=True)
name = db.Column(db.String(200), nullable=False, unique=True)
capital = db.relationship('Capital', uselist=False)
Capital:一
1
2
3
4
5
6
class Capital(db.Model):
__tablename__ = 'capital'
id = db.Column(db.Integer, primery_key=True, autoincrement=True)
name = db.Column(db.String(200), nullable=False)
country_id = db.Column(db.Integer,db.ForeignKey('country.id'))
country = db.relationship('Country')

}

association_tbale:辅助表
1
2
3
4
5
association_table = db.Table(
'association',
db.Column('student_id',db.Integer,db.ForeignKey('student.id')),
db.Column('teacher_id',db.Integer,db.ForeignKey('teacher.id'))
)
StudentModel:多
1
2
3
4
5
6
7
8
class Student(db.Model):
__tablename__ = 'student'
id = db.Column(db.Integer, primery_key=True, autoincrement=True)
teachers = db.relationship(
'Teacher',
secondary = association_table,
back_populates = 'students'
)
TeacherModel:多
1
2
3
4
5
6
7
8
class Teacher(db.Model):
__tablename__ = 'teacher'
id = db.Column(db.Integer, primery_key=True, autoincrement=True)
students = db.relationship(
'Student',
secondary= association_table,
back_populates='teachers'
)

基本数据库操作

需要写入数据库的操作都需要在最后进行commit提交

1
db.session.commit()

db.session.add

1
2
3
book = Book(name=name, author_id=author)
db.session.add(book)
db.session.commit()

db.session.delete

1
2
3
book = Book.query.get(id)
db.session.delete(book)
db.session.commit()
1
2
3
book = Book.query.get(id)
book.name = new_name
db.session.commit()

Obj.query是继承自db.Model的属性,
查询需要调用ORM模型的query属性

get(primary_key) 通过主键查询

1
book = Book.query.get()

filter_by(key=value,…)

注意: filter_by()[0]和filter_by().first()的区别:

  • filter_by[0] 为空时会报错
  • filter_by().first() 为空时不会报错
1
book = Book.query.filter_by(name=name)