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>
相关推荐
人间打气筒(Ada)1 分钟前
如何基于 Go-kit 开发 Web 应用:从接口层到业务层再到数据层
开发语言·后端·golang
Luke Ewin2 分钟前
Linux中部署Qwen3.5大模型
linux·运维·服务器·ai·llm·qwen3.5
2501_924952694 分钟前
代码生成器优化策略
开发语言·c++·算法
wan9yu11 分钟前
为什么你需要给 LLM 的数据"加密"而不是"脱敏"?我写了一个开源工具
python
清风徐来QCQ15 分钟前
八股文(1)
java·开发语言
lsx20240618 分钟前
网站主机技术
开发语言
摇滚侠20 分钟前
你是一名 java 程序员,总结定义数组的方式
java·开发语言·python
xyq202430 分钟前
Vue3 条件语句详解
开发语言
这个名有人用不39 分钟前
解决 uv 虚拟环境使用 pip 命令提示command not found的办法
python·pip·uv·claude code