章节索引 :

Flask 的 ORM 模型-应用

在上一个小节中,讲解了 ORM 模型的基本概念,并给出一个最小的例子说明如何建立面向对象与关系数据库之间的映射关系。本小节介绍了 SqlAlchemy 的相关 API,通过一个完整的例子,对 mysql 数据库进行增、删、改、查。

1. 功能概述

在本小节,编写程序 app.py,对一个名称为 school 的数据库进行增、删、改、查,数据库中存在一张表 students,包含的字段如下:

字段名 类型 描述
sno 整数 学号
name 字符串 姓名
age 整数 年龄

在上一个小节中,使用 mysql 的命令行创建表 students,在本节的例子中,为了展示 ORM 的功能,以编程的方式创建表 students。

2. 配置 SQLAlchemy

首先,引入相关库,对访问 mysql 进行配置,如下所示:

#!/usr/bin/python3
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import sys

app = Flask(__name__)
user = 'root'
password = '123456' 
database = 'school'
uri = 'mysql+pymysql://%s:%s@localhost:3306/%s' % (user, password, database)
app.config['SQLALCHEMY_DATABASE_URI'] = uri 

db = SQLAlchemy(app)

在第 1 行到第 4 行,引入库 flask 和 flask_sqlalchemy;在第 6 行到第 11 行,对 SQLAlchemy 进行配置,设置如下参数:

参数
user 访问数据库的用户,假设是 root
password 访问数据库的密码,假设是 123456
database 数据库名称
uri SQLAlchemy 连接数据库的字符串

在第 8 行,对 SQLAlchemy 进行配置,SQLALCHEMY_DATABASE_URI 配置的是连接数据库的字符串,在这个例子中,该字符串为:

mysql+pymysql://root:123456@localhost:3306/school

字符串中的 “mysql+pymysql” 表示:数据库类型是 mysql,使用 pymysql 作为访问 mysql 的底层 API。

最后,在第 13 行,创建 SQLAlchemy 对象,用于映射数据库表和对象。

3. 程序框架

程序包含有 4 个主要功能:

函数名 功能
create_table 创建表 students
insert_students 在表 students 中插入数据
query_students 查询表 students
update_students 更新表 students 中的数据
delete_students 删除表 students 中的数据
class Student(db.Model):
    pass

def create_table():
    pass

def insert_students():
    pass

def query_students():
    pass

def update_students():
    pass

def delete_students():
    pass

command = sys.argv[1]
if command == 'create':
    create_table()
elif command == 'insert':
    insert_students()
elif command == 'query':
    query_students()
elif command == 'update':
    update_students()
elif command == 'delete':
    delete_students()

首先,定义继承于 db.Model 的类 Student,该类映射数据库中的表 students;然后,分别定义了实现上述功能的函数,在后续小节会陆续填充;最后,例子程序是一个命令行程序,根据不同的命令行参数调用相应的功能函数。

4. 创建表

class Student(db.Model):
    __tablename__ = 'students'
    sno = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    age = db.Column(db.Integer)

    def dump(self):
        print(self.sno, self.name, self.age)

def create_table():
    db.drop_all()
    db.create_all()

首先,建立表和类的映射关系:创建类 Student 继承于 db.Model,表示类 Student 用于映射数据库中的表;设定 __tablename__ 为 students,表示将类 Student 映射到数据库中的表 students。

然后,建立属性和字段的映射关系:映射 sno 到表 students 的字段 sno,类型为整数 (db.Integer),primary_key=True 表示该字段是主键; 映射 name 到表 students 的字段 name,类型为整数 (db.String); 映射 age 到表 students 的字段 age,类型为整数 (db.Integer)。

调用 db.drop_all() 删除数据库 school 中的所有表格;调用 db.create_all() 创建已经建立映射关系的表 students,表 students 已经被映射到类 Student。

5. 插入数据

def insert_students():
    tom = Student(sno = 1, name = 'tom', age = 12)
    db.session.add(tom)
    db.session.commit()

    jerry = Student(sno = 2, name = 'jerry', age = 11)
    mike = Student(sno = 3, name = 'mike', age = 11)
    db.session.add_all([jerry, mike])
    db.session.commit()

在第 2 行,通过类 Student 实例化生成一个实例 tom,调用 db.session.add(tom) 将该实例加入到数据库连接会话中,调用 db.session.commit() 提交保存到数据库。

在第 6 行和第 7 行,生成 2 个实例 jerry 和 mike,调用 db.session.add_all([jerry, mike]) 将两个实例批量加入到数据库连接会话中,调用 db.session.commit() 提交保存到数据库。

6. 查询数据

完成查询表 students 的函数 query_students,该函数包括 4 个片段:

6.1 查询所有的学生

def query_students():
    print('查询所有的学生')
    students = Student.query.all()
    for student in students: 
        student.dump()
    print()

类 Student 继承于类 db.Model,继承了方法 db.Model.query.all(),该方法查询表中所有的数据,无条件返回表中所有的数据。

类 Student 映射为表 students,Student.query.all() 返回表 students 中所有的学生数据。

6.2 指定条件查询

继续在函数 query_students 中增加如下代码:

    print('查询所有年龄是 11 岁的学生')
    students = Student.query.filter_by(age = 11)
    for student in students: 
        student.dump()
    print()

类 Student 继承于类 db.Model,继承了方法 db.Model.query.filter_by(conidtion),该方法的参数 condition 是查询条件,返回表中符合条件的数据。

类 Student 映射为表 students,Student.filter_by(age = 11) 指明查询条件为 age = 11, 返回表 students 中所有年龄是 11 岁的学生。

6.3 查询第一个符合条件的数据

继续在函数 query_students 中增加如下代码:

    print('查询第一个年龄是 11 岁的学生')
    students = Student.query.filter_by(age = 11)
    student = students.first()
    student.dump()
    print()

方法 db.Model.query.filter_by(conidtion) 返回一个集合,包括所有满足条件的数据。方法 first() 返回第一个符合条件的数据。

6.4 根据条件组合查询

继续在函数 query_students 中增加如下代码:

    print('查询姓名是 jerry 并且年龄是 11 岁的学生')
    students = Student.query.filter_by(age = 11, name = 'jerry')
    for student in students:
        student.dump()
    print()

在方法 db.Model.query.filter_by(conidtion) 中,参数 condition 可以是多个。filter_by(age = 11, name = ‘jerry’) 表示查询姓名是 jerry 并且年龄是 11 岁的学生。

7. 更新数据

def update_students():
    students = Student.query.filter_by(name = 'tom')
    students.update({'name':'TOM'})
    db.session.commit()

类 Student 映射为表 students,Student.filter_by(name = ‘tom’) 指明查询条件为 name = ‘tom’, 返回表 students 中所有姓名是 tom 的学生。

调用 update({‘name’: ‘TOM’}) 方法,将所有姓名是 tom 的学生的姓名更改为 TOM,调用 db.session.commit() 提交保存到数据库。

8. 删除数据

def delete_students():
    students = Student.query.filter_by(name = 'mike')
    students.delete()
    db.session.commit()

类 Student 映射为表 students,Student.filter_by(name = ‘mike’) 指明查询条件为 name = ‘mike’, 返回表 students 中所有姓名是 mike 的学生。

调用 delete() 方法,将所有姓名是 mike 的学生从表 students 中删除,调用 db.session.commit() 提交保存到数据库。

9. 测试程序

1. 在 mysql 命令行中创建数据库 school

$ sudo mysql
mysql > drop database school;
mysql > create database school;

2. 创建表 students

$ python3 app.py create

3. 向表 students 插入数据

$ python3 app.py insert

4. 查询表 students 中的数据

$ python3 app.py query
查询所有的学生
1 tom 12
2 jerry 11
3 mike 11

查询所有年龄是 11 岁的学生
2 jerry 11
3 mike 11

查询第一个年龄是 11 岁的学生
2 jerry 11

查询姓名是 jerry 并且年龄是 11 岁的学生
2 jerry 11

结果显示,向数据库中插入了 3 个学生:tom、jerry 和 mike。

5. 更新表 students 中的数据

$ python3 app.py update
$ python3 app.py query
查询所有的学生
1 TOM 12
2 jerry 11
3 mike 11
...

结果显示,姓名为 tom 的学生的姓名更改为 TOM。

6. 删除表 students 中的数据

$ python3 app.py delete
$ python3 app.py query
查询所有的学生
1 TOM 12
2 jerry 11
...

结果显示,姓名为 mike 的学生被删除。

5. 源代码下载

6. 小结

本小节通过一个具体的实例讲解了 SqlAlchemy 的相关 API,使用思维导图概括如下:

图片描述

通过本节的例子,可以看出,与拼接 SQL 语句访问数据库相比,通过 ORM 进行数据库访问,显著的提高了代码的可读性。