Flask 的 ORM 模型-應(yīng)用
在上一個小節(jié)中,講解了 ORM 模型的基本概念,并給出一個最小的例子說明如何建立面向?qū)ο笈c關(guān)系數(shù)據(jù)庫之間的映射關(guān)系。本小節(jié)介紹了 SqlAlchemy 的相關(guān) API,通過一個完整的例子,對 mysql 數(shù)據(jù)庫進(jìn)行增、刪、改、查。
1. 功能概述
在本小節(jié),編寫程序 app.py
,對一個名稱為 school 的數(shù)據(jù)庫進(jìn)行增、刪、改、查,數(shù)據(jù)庫中存在一張表 students,包含的字段如下:
字段名 | 類型 | 描述 |
---|---|---|
sno | 整數(shù) | 學(xué)號 |
name | 字符串 | 姓名 |
age | 整數(shù) | 年齡 |
在上一個小節(jié)中,使用 mysql 的命令行創(chuàng)建表 students,在本節(jié)的例子中,為了展示 ORM 的功能,以編程的方式創(chuàng)建表 students。
2. 配置 SQLAlchemy
首先,引入相關(guān)庫,對訪問 mysql 進(jìn)行配置,如下所示:
#!/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 進(jìn)行配置,設(shè)置如下參數(shù):
參數(shù) | 值 |
---|---|
user | 訪問數(shù)據(jù)庫的用戶,假設(shè)是 root |
password | 訪問數(shù)據(jù)庫的密碼,假設(shè)是 123456 |
database | 數(shù)據(jù)庫名稱 |
uri | SQLAlchemy 連接數(shù)據(jù)庫的字符串 |
在第 8 行,對 SQLAlchemy 進(jìn)行配置,SQLALCHEMY_DATABASE_URI 配置的是連接數(shù)據(jù)庫的字符串,在這個例子中,該字符串為:
mysql+pymysql://root:123456@localhost:3306/school
字符串中的 “mysql+pymysql” 表示:數(shù)據(jù)庫類型是 mysql,使用 pymysql 作為訪問 mysql 的底層 API。
最后,在第 13 行,創(chuàng)建 SQLAlchemy 對象,用于映射數(shù)據(jù)庫表和對象。
3. 程序框架
程序包含有 4 個主要功能:
函數(shù)名 | 功能 |
---|---|
create_table | 創(chuàng)建表 students |
insert_students | 在表 students 中插入數(shù)據(jù) |
query_students | 查詢表 students |
update_students | 更新表 students 中的數(shù)據(jù) |
delete_students | 刪除表 students 中的數(shù)據(jù) |
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,該類映射數(shù)據(jù)庫中的表 students;然后,分別定義了實現(xiàn)上述功能的函數(shù),在后續(xù)小節(jié)會陸續(xù)填充;最后,例子程序是一個命令行程序,根據(jù)不同的命令行參數(shù)調(diào)用相應(yīng)的功能函數(shù)。
4. 創(chuàng)建表
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()
首先,建立表和類的映射關(guān)系:創(chuàng)建類 Student 繼承于 db.Model,表示類 Student 用于映射數(shù)據(jù)庫中的表;設(shè)定 __tablename__ 為 students,表示將類 Student 映射到數(shù)據(jù)庫中的表 students。
然后,建立屬性和字段的映射關(guān)系:映射 sno 到表 students 的字段 sno,類型為整數(shù) (db.Integer),primary_key=True 表示該字段是主鍵; 映射 name 到表 students 的字段 name,類型為整數(shù) (db.String); 映射 age 到表 students 的字段 age,類型為整數(shù) (db.Integer)。
調(diào)用 db.drop_all() 刪除數(shù)據(jù)庫 school 中的所有表格;調(diào)用 db.create_all() 創(chuàng)建已經(jīng)建立映射關(guān)系的表 students,表 students 已經(jīng)被映射到類 Student。
5. 插入數(shù)據(jù)
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,調(diào)用 db.session.add(tom) 將該實例加入到數(shù)據(jù)庫連接會話中,調(diào)用 db.session.commit() 提交保存到數(shù)據(jù)庫。
在第 6 行和第 7 行,生成 2 個實例 jerry 和 mike,調(diào)用 db.session.add_all([jerry, mike]) 將兩個實例批量加入到數(shù)據(jù)庫連接會話中,調(diào)用 db.session.commit() 提交保存到數(shù)據(jù)庫。
6. 查詢數(shù)據(jù)
完成查詢表 students 的函數(shù) query_students,該函數(shù)包括 4 個片段:
6.1 查詢所有的學(xué)生
def query_students():
print('查詢所有的學(xué)生')
students = Student.query.all()
for student in students:
student.dump()
print()
類 Student 繼承于類 db.Model,繼承了方法 db.Model.query.all(),該方法查詢表中所有的數(shù)據(jù),無條件返回表中所有的數(shù)據(jù)。
類 Student 映射為表 students,Student.query.all() 返回表 students 中所有的學(xué)生數(shù)據(jù)。
6.2 指定條件查詢
繼續(xù)在函數(shù) query_students 中增加如下代碼:
print('查詢所有年齡是 11 歲的學(xué)生')
students = Student.query.filter_by(age = 11)
for student in students:
student.dump()
print()
類 Student 繼承于類 db.Model,繼承了方法 db.Model.query.filter_by(conidtion),該方法的參數(shù) condition 是查詢條件,返回表中符合條件的數(shù)據(jù)。
類 Student 映射為表 students,Student.filter_by(age = 11) 指明查詢條件為 age = 11, 返回表 students 中所有年齡是 11 歲的學(xué)生。
6.3 查詢第一個符合條件的數(shù)據(jù)
繼續(xù)在函數(shù) query_students 中增加如下代碼:
print('查詢第一個年齡是 11 歲的學(xué)生')
students = Student.query.filter_by(age = 11)
student = students.first()
student.dump()
print()
方法 db.Model.query.filter_by(conidtion) 返回一個集合,包括所有滿足條件的數(shù)據(jù)。方法 first() 返回第一個符合條件的數(shù)據(jù)。
6.4 根據(jù)條件組合查詢
繼續(xù)在函數(shù) query_students 中增加如下代碼:
print('查詢姓名是 jerry 并且年齡是 11 歲的學(xué)生')
students = Student.query.filter_by(age = 11, name = 'jerry')
for student in students:
student.dump()
print()
在方法 db.Model.query.filter_by(conidtion) 中,參數(shù) condition 可以是多個。filter_by(age = 11, name = ‘jerry’) 表示查詢姓名是 jerry 并且年齡是 11 歲的學(xué)生。
7. 更新數(shù)據(jù)
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 的學(xué)生。
調(diào)用 update({‘name’: ‘TOM’}) 方法,將所有姓名是 tom 的學(xué)生的姓名更改為 TOM,調(diào)用 db.session.commit() 提交保存到數(shù)據(jù)庫。
8. 刪除數(shù)據(jù)
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 的學(xué)生。
調(diào)用 delete() 方法,將所有姓名是 mike 的學(xué)生從表 students 中刪除,調(diào)用 db.session.commit() 提交保存到數(shù)據(jù)庫。
9. 測試程序
1. 在 mysql 命令行中創(chuàng)建數(shù)據(jù)庫 school
$ sudo mysql
mysql > drop database school;
mysql > create database school;
2. 創(chuàng)建表 students
$ python3 app.py create
3. 向表 students 插入數(shù)據(jù)
$ python3 app.py insert
4. 查詢表 students 中的數(shù)據(jù)
$ python3 app.py query
查詢所有的學(xué)生
1 tom 12
2 jerry 11
3 mike 11
查詢所有年齡是 11 歲的學(xué)生
2 jerry 11
3 mike 11
查詢第一個年齡是 11 歲的學(xué)生
2 jerry 11
查詢姓名是 jerry 并且年齡是 11 歲的學(xué)生
2 jerry 11
結(jié)果顯示,向數(shù)據(jù)庫中插入了 3 個學(xué)生:tom、jerry 和 mike。
5. 更新表 students 中的數(shù)據(jù)
$ python3 app.py update
$ python3 app.py query
查詢所有的學(xué)生
1 TOM 12
2 jerry 11
3 mike 11
...
結(jié)果顯示,姓名為 tom 的學(xué)生的姓名更改為 TOM。
6. 刪除表 students 中的數(shù)據(jù)
$ python3 app.py delete
$ python3 app.py query
查詢所有的學(xué)生
1 TOM 12
2 jerry 11
...
結(jié)果顯示,姓名為 mike 的學(xué)生被刪除。
5. 源代碼下載
6. 小結(jié)
本小節(jié)通過一個具體的實例講解了 SqlAlchemy 的相關(guān) API,使用思維導(dǎo)圖概括如下:
通過本節(jié)的例子,可以看出,與拼接 SQL 語句訪問數(shù)據(jù)庫相比,通過 ORM 進(jìn)行數(shù)據(jù)庫訪問,顯著的提高了代碼的可讀性。