Flask教程5:flask数据库SQLAlchemy

文章目录

SQLAlchemy
  • SQLAlchemy是一个基于Python实现的ORM (Object Relational Mapping,对象关系映射)框架。该框架建立在DB API (数据库应用程序接口系统) 之上,使用关系对象映射进行数据库操作。简言之便是将类和对象转换成SQL,然后使用数据API (接口) 执行SQL 并获取执行结果。

它的核心思想于在于将关系数据库表中的记录映射成为对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作。

对象-关系的映射(ORM)实质

  • SQL语句如下:
sql 复制代码
# 建立表book
create table book('id' int(11) NOT NULL AUTO_INCREMENT, 'tiltle' varchar(50),'publishing_office' varchar(100),'isbn' varchar(4));
  • book对象如下:
python 复制代码
# 使用SQLALchemy创建表book
class Book(db.Model):
    __tablename__='book'
    id = db.Column(db.Integer, primary_key = True,autoincrement = True)	#定义id字段
    title = db.Column(db.String(50),nullable = False)	#定义title字段
    publishing_office = db.Column(db.String(100),nullable = False)	#定义出版社字段
    isbn = db.Column(db.String(100),nullable = False) #定义isbn号字段
    storage_time = db.Column(db.DateTime, default = datetime.now) # 入库时间字段
  • Flask-SQLALchemyORM框架便可以实现将操作数据库转变为操作对象,一个book表被抽象成了一个Book类,一个表中的id、tiltle、publishing_office、isbn、storage_time字段被抽象成一个类的五个属性,而该表的一条数据记录就抽象成该类的一个实例化对象,不用再写烦琐的底层SQL语句了。
为什么使用ORM

当需要实现一个应用程序时,如果不使用ORM,我们可能会写特别多的数据访问层的代码,从数据库保存、删除、读取对象信息,但这些代码都是重复的。如果使用ORM则能够大大减少重复性的代码。

对象关系映射(Object Relational Mapping,ORM),主要实现程序对象到关系数据库数据的映射,具有以下特点:

  • 简单:ORM以最基本的形式建模数据。比如 ORM会将MySQL 的一张表映射成一个类(模型),表的字段就是这个类的成员变量(属性)。
  • 精确:ORM 使所有的 MySQL 数据表都按照统一的标准精确地映射成一个类,使系统在代码层面保持准确统一。
  • 易懂:ORM使数据库结构文档化,程序员可以把大部分精力用在Web功能的开发和实现上,而不需要花费时间和精力在底层数据库驱动上。
  • 易用:ORM包含对持久类对象进行CRUD操作的API,例如:create()、update()、save()、load()、find()、find_all()和 where()等,也就是将SQL查询全部封装成了编程语言中的函数,通过函数的链式组合生成最终的SQL语句。通过这种封装避免了不规范、冗余、风格不统一的SQL语句,可以避免很多人为 Bug,方便编码风格的统一和后期维护。

综上所述,使用ORM框架的最大优点是解决了重复读取数据库的问题,使程序员高效开发成为可能。最大的不足之处在于会牺牲程序的执行效率,特别是处理多表联查、where条件复杂之类的查询时,ORM 的语法会变得复杂。

初始化数据库配置

要使用SQLAlchemy连接数据库,必须要进行必要的初始化配置后才能实现,数据库配置文件一般要求独立成一个文件,便于管理和移植;

配置文件:config.py

python 复制代码
USERNAME = 'root' #设置登录账号
PASSWORD = '123456' #设置登录密码
HOST = '127.0.0.1' #设置主机地址
PORT = '3306' #设置端口号
DATABASE ='flaskdb' #设置访问的数据库

# 创建URI(统一资源标志符)
'''
SQLALCHEMY_DATABASE_URI的固定格式为:
'{数据库管理系统名}://{登录名}:{密码}@{IP地址}:{端口号}/{数据库名}?charset={编码格式}'
'''
DB_URI = 'mysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME,PASSWORD,HOST,PORT,DATABASE)

# 设置数据库的连接URI
SQLALCHEMY_DATABASE_URI = DB_URI
# 设置动态追踪修改,如未设置只会提示警告
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 设置查询时会显示原始SQL语句
SQLALCHEMY_ECHO = True

上述配置文件设置完后,在flask程序文件下导入该文件,再用app.config.from_object方法导入到flask对象内即可;

当然上述配置也可以直接在flask程序文件内设置,但是不利于后期管理;

在完成配置的设置与导入后,可以用下面的代码检测配置是否成功。如果SQLAlchemy配置成功,程序运行将会不报错并正常返回一个服务器url

sql 复制代码
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import flask学习.config # 导入配置文件

app = Flask(__name__)
# 导入配置文件至flask对象
app.config.from_object(flask学习.config)

'''1. 直接用键值对插入配置:(使用 localhost 替代 127.0.1:3306)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:123456@localhost/flaskdb?charset=utf8'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SQLALCHEMY_ECHO'] = True
'''

'''2. 定义配置类后导入:(使用 localhost 替代 127.0.1:3306)
class Config:
    SQLALCHEMY_DATABASE_URI = 'mysql://root:123456@localhost/flaskdb?charset=utf8'
    SQLALCHEMY_TRACK_MODIFICATIONS = True
    SQLALCHEMY_ECHO = True

app.config.from_object(Config)
'''

# 初始化一个SQLAlchemy对象
db = SQLAlchemy(app)
# 测试数据库连接是否成功(create_all将我们定义的所有表类映射为数据库下的表)
db.create_all()

@app.route('/')
def index():
    return 'index'

if __name__=='__main__':
    app.run(debug=True)
表模型的定义与数据库映射

SQLAlchemy允许我们依据数据库的表结构来构建数据模型,即用python类的方式定义一个表模型,再通过调用create_all()方法,就可以将我们定义的所有表模型全部映射为数据库下的表;

但要注意的是每次运行create_all()这行代码时都会将所有表创建一次,导致重复创建表的问题,可以在前面添加一行drop_all()代码来删除已经创建的表;

在用类的方式定义表模型时,使用__tablename__='<表名>'来将字符串对象设为表名,使用<列名>=db.Column()来将Column类的实例对象设为表的字段;我们为字段(列)指定的数据类型和约束条件,作为Column实例化时的参数;

  • 利用上述知识点,就可以实现SQLAlchemy创建表了,并可以尝试着进行插入记录的操作SQLAlchemy.session.add()
python 复制代码
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import flask学习.config
from datetime import * # datatime库下的datatime类重名了

app = Flask(__name__)
app.config.from_object(flask学习.config)

# 初始化一个SQLAlchemy对象(该步要在导入config后执行)
# 实例化的同时将与数据库相关的配置读入
db = SQLAlchemy(app)
# 初始化app对象中与数据库相关的配置的设置,防止数据库信息泄露
db.init_app(app)

# 创建表模型类对象
class Book(db.Model):
    __tablename__='book'
    id = db.Column(db.Integer, primary_key = True,autoincrement = True)	#定义id字段
    title = db.Column(db.String(50),nullable = False)	#定义title字段
    publishing_office = db.Column(db.String(100),nullable = False)	#定义出版社字段
    isbn = db.Column(db.String(100),nullable = False) #定义isbn号字段
    storage_time = db.Column(db.DateTime, default = datetime.now) # 入库时间字段

if __name__ == '__main__':
    # 删除数据库下的所有上述定义的表,防止重复创建
    db.drop_all()
    # 将上述定义的所有表对象映射为数据库下的表单(创建表)
    db.create_all()

    # 向表中插入记录:实例化-插入-提交
    book1 = Book(id='001',title='人工智能导论',publishing_office ='高等教育出版社',isbn='9787040479843')
    db.session.add(book1)
    db.session.commit()
数据的增、删、改、查操作
python 复制代码
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import flask学习.config
from datetime import * # datatime库下的datatime类重名了

app = Flask(__name__)
app.config.from_object(flask学习.config)

# 初始化一个SQLAlchemy对象(该步要在导入config后执行)
# 实例化的同时将与数据库相关的配置读入
db = SQLAlchemy(app)
# 初始化app对象中与数据库相关的配置的设置,防止数据库连接泄露
db.init_app(app)

# 创建表模型类对象
class Book(db.Model):
    __tablename__='book'
    id = db.Column(db.Integer, primary_key = True,autoincrement = True)	# 定义id字段
    title = db.Column(db.String(50),nullable = False)	                # 定义title字段
    publishing_office = db.Column(db.String(100),nullable = False)	    # 定义出版社字段
    price = db.Column(db.String(30), nullable=False)                    # 定义price号字段
    isbn = db.Column(db.String(50),nullable = False)                    # 定义isbn号字段
    storage_time = db.Column(db.DateTime, default = datetime.now)       # 入库时间字段

# 删除数据库下的所有上述定义的表,防止重复创建
db.drop_all()
# 将上述定义的所有表对象映射为数据库下的表单(创建表)
db.create_all()

# 添加数据的路由
@app.route('/add')
def add_record():
    book1 = Book(title='Python基础教程(第3版)', publishing_office ='人民邮电出版社', price = '68.30 ', isbn = '9787115474889')
    book2= Book(title='Python游戏编程快速上手第4版',publishing_office = '人民邮电出版社', price = '54.50', isbn = '9787115466419')
    book3 = Book(title='JSP+Servlet+Tomcat应用开发从零开始学',publishing_office = '清华大学出版社', price = '68.30', isbn = '9787302384496')

    db.session.add(book1)
    db.session.add(book2)
    db.session.add(book3)
    # 需要提交事务给数据库
    db.session.commit()
    return 'add success!'

# 查找数据的路由
@app.route('/query')
def query_record():
    # 查找id=1的第一个对象
    result = Book.query.filter(Book.id == '1').first()
    print(result.title)
    # 查找publishing_office=人民邮电出版社的全体对象
    result_list = Book.query.filter(Book.publishing_office == '人民邮电出版社').all()
    for books in result_list:
        print(books.title)
    return 'query success!'

# 修改数据的路由
@app.route('/edit')
def edit_record():
    # 查找id=1的第一个对象
    book1 = Book.query.filter(Book.id == '1').first()
    book1.price = 168
    # 需要提交事务给数据库
    db.session.commit()
    return 'edit success!'

# 删除数据的路由
@app.route('/delete')
def delete_record():
    # 查找id=9的第一个对象
    book2 = Book.query.filter(Book.id == '9').first()
    db.session.delete(book2)
    # 需要提交事务给数据库
    db.session.commit()
    return 'delete success!'

if __name__ == '__main__':
    app.run(debug=True)
数据的添加

确保数据库已经建立好后,直接使用db.session.add(<实例化对象>)的方法就可以把依据某一个表类实例化的数据对象插入到对应表中;

这一步操作需要进行事务提交db.session.commit();

这里用一个路由来完成插入操作:

python 复制代码
# 添加数据的路由
@app.route('/add')
def add_record():
    book1 = Book(title='Python基础教程(第3版)', publishing_office ='人民邮电出版社', price = '68.30 ', isbn = '9787115474889')
    book2= Book(title='Python游戏编程快速上手第4版',publishing_office = '人民邮电出版社', price = '54.50', isbn = '9787115466419')
    book3 = Book(title='JSP+Servlet+Tomcat应用开发从零开始学',publishing_office = '清华大学出版社', price = '68.30', isbn = '9787302384496')
	# 以列表形式添加必须使用add_all,错误使用add将会报错:Class 'builtins.list' is not mapped
    # db.session.add_all([book1,book2,book3])
    db.session.add(book1)
    db.session.add(book2)
    db.session.add(book3)
    # 需要提交事务给数据库
    db.session.commit()
    return 'add success!'
数据的查找

数据的查找需要通过query.filter(<限制条件>)方法来实现,query继承自db.Modelquery.filter返回的是查找到的所有满足限制条件的数据对象组成的列表,当没有限制条件时则返回表中所有记录对应的数据对象组成的列表;

可以使用first()来获取查找到的第一个数据对象,也可以用all()来获取查找到的全部数据对象(一个列表);

这里也用一个路由来完成查找操作:

python 复制代码
# 查找数据的路由
@app.route('/query')
def query_record():
    # 查找id=1的第一个对象
    result = Book.query.filter(Book.id == '1').first()
    print(result.title)
    # 查找publishing_office=人民邮电出版社的全体对象
    result_list = Book.query.filter(Book.publishing_office == '人民邮电出版社').all()
    for books in result_list:
        print(books.title)
    return 'query success!'
数据的修改

数据的修改操作是基于数据的查找操作实现的,先通过查找记录获取到对应的数据对象,再对该对象的某一个字段进行修改即可;

这一步操作需要进行事务提交db.session.commit();

这里也用一个路由来完成修改操作:

python 复制代码
# 修改数据的路由
@app.route('/edit')
def edit_record():
    # 查找id=1的第一个对象
    book1 = Book.query.filter(Book.id == '1').first()
    book1.price = 168
    # 需要提交事务给数据库
    db.session.commit()
    return 'edit success!'
数据的删除

数据的删除和数据的修改一样,也是基于数据的查找实现的,先通过查找记录获取到对应的数据对象,再调用db.session.delete(<数据对象>)方法即可删除该数据对象与表中的记录;

这一步操作需要进行事务提交db.session.commit();

这里仍用一个路由来完成删除操作:

python 复制代码
# 删除数据的路由
@app.route('/delete')
def delete_record():
    # 查找id=9的第一个对象
    book2 = Book.query.filter(Book.id == '9').first()
    db.session.delete(book2)
    # 需要提交事务给数据库
    db.session.commit()
    return 'delete success!'
init_app作用详解

在数据增删改查部分,我们使用到了一行代码:db.init_app()

关于init_app(),官方文档给出的解释是这样的:This callback can be used to initialize an application for the use with this database setup. Never use a database in the context of an application not initialized that way or connections will leak.

意思是说:此回调函数可将应用程序中对于此数据库的设置进行初始化。切勿在未以这种方式初始化的应用程序上下文中使用数据库,否则(数据库)连接将泄漏。

python 复制代码
import flask学习.config

app.config.from_object(flask学习.config)
db = SQLAlchemy(app)
print(db)
-----------------运行结果---------------
<SQLAlchemy engine=mysql://root:***@127.0.0.1:3306/flaskdb?charset=utf8>
相关推荐
子龙烜3 分钟前
数据分析三剑客-Matplotlib
python·数据挖掘·数据分析·matplotlib
图灵追慕者4 分钟前
python绘制领域矩形
开发语言·python·领域
草丛中的蝈蝈8 分钟前
ubuntu16.04上搭建qt开发环境
开发语言·qt
番茄炒西红柿炒蛋8 分钟前
秋招Java后端开发冲刺——非关系型数据库篇(Redis)
数据库·redis·nosql
PPPPPaPeR.17 分钟前
TopK问题与堆排序
c语言·开发语言·c++·算法
Recitative19 分钟前
python单元测试入门
人工智能·python·深度学习·机器学习·单元测试
小安同学iter23 分钟前
计算机大方向的选择
python·django·pygame
苏十八28 分钟前
前端基础:JavaScript(篇一)
开发语言·前端·javascript·面试·html·ecmascript·html5
人才程序员32 分钟前
【Rust入门】猜数游戏
开发语言·c++·后端·单片机·游戏·rust·c
modelsetget32 分钟前
MySQL锁机制详细笔记
数据库·mysql·面试·数据库锁