Flask-SQLAIchemy和Flask-Migrate扩展的配置与使用

Flask-SQLAIchemy扩展的配置与使用

在Flask中,所有扩展插件基本上都是Flask-Xxx进行命名,Flask-SQLAIchemy就是一个为Flask应用增加SQLAIchemy支持的扩展,致力于简化在Flask中SQLAIchemy的使用,而SQLAIchemy是目前Python中最强大的ORM框架,功能全面,使用简单。

文档资料:

  1. Flask-SQLAlchemy 文档:https://flask-sqlalchemy.palletsprojects.com/en/3.1.x/
  2. SQLAlchemy 文档:https://www.sqlalchemy.org/

ORM 是什么

ORM 其实是 对象映射关系(Object-Relational Mapping),即将数据库中的表与面向对象编程中的类关联起来,它把数据库中的表映射为类,表中的行映射为类的实例,表中的列映射为类的属性。这样一来,就可以通过对类的操作来进行数据库的增删改查,而不必直接操作数据库,让程序员更加专注于业务逻辑,减少了与数据库交互的复杂性。

例如有一张基础表为 account,字段信息如下:

sql 复制代码
CREATE TABLE "account" (
    "id" uuid NOT NULL,
    "name" varchar(255) NOT NULL,
    "email" varchar(255) NOT NULL,
    "avatar" varchar(255) NOT NULL,
    "password" varchar(255) NOT NULL,
    "updated_at" timestamp(6) NOT NULL,
    "created_at" timestamp(6) NOT NULL,
);

将其映射到 Python 的 Class 为:

python 复制代码
import uuid
from datetime import datetime
from sqlalchemy import Column, String, DateTime, UUID, PrimaryKeyConstraint, Index
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Account(db.Model):
    __tablename__ = "account"
    __table_args__ = (
        PrimaryKeyConstraint("id", name="pk_account_id"),
        Index("idx_account_email", "email"),
    )

    id = Column(UUID, default=uuid.uuid4, nullable=False)
    name = Column(String(255), default="", nullable=False)
    email = Column(String(255), default="", nullable=False)
    avatar = Column(String(255), default="", nullable=False)
    password = Column(String(255), default="", nullable=False)
    updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, nullable=False)
    created_at = Column(DateTime, default=datetime.now, nullable=False)

这样,在 Python 中,只需要对映射类进行特定方法的调用,即可实现数据库的增删改查,伪代码示例如下:

python 复制代码
# 类映射伪代码
account = Account(id="xxx-xxx-xxx-xxx")

account.add()    # 增:映射到SQL中的 create table xxx (xxx, xxx, xxx);
account.delete() # 删:映射到SQL中的 delete from xxx where xxx;
account.update() # 改:映射到SQL中的 update table set xxx=xxx, xxx=xxx where xxx;
account.get()    # 查:映射到SQL中的 select * from xxx where xxx;

通过 ORM 可以在代码中少写甚至不写 SQL,去实现数据库的增删改查等复杂业务,同时确保 SQL 安全,不被注入。从上面可以看出 ORM 的优缺点其实非常明显:

  • 优点
    1. 有语法提示,省去自己拼写 SQL,保证 SQL 语法的正确性;
    2. ORM 提供方言功能(dialect,可以转换为多种数据库语法),减少学习成本与迁移数据库的成本;
    3. 面向对象,可读性强,开发效率高;
    4. 防止 SQL 注入攻击;
    5. 搭配数据库迁移,更新数据库方便;
  • 缺点
    1. 需要语法转换,效率比原生 SQL 低;
    2. 复杂的查询往往语法比较复杂(可以使用原生 SQL 代替);

Flask-SQLAlchemy 环境安装

使用 pip 可以直接安装 Flask-SQLAlchemy,命令如下:

powershell 复制代码
pip install flask-sqlalchemy

期间如果提示 ModuleNotFoundError: No module named 'psycopg2',是因为使用 Postgres 作为数据库,但缺少驱动引擎导致的,安装对应的驱动引擎即可,命令如下:

bash 复制代码
pip install psycopg2

也可以一次性安装两个依赖:

bash 复制代码
pip install flask-sqlalchemy psycopg2

和其他的 Flask 扩展一样,几乎绝大部分扩展都需要进行初始化与配置,Flask-SQLAlchemy 也一样。Flask-SQLAlchemy 的相关配置封装到了 Flask 的配置项中,可以通过 app.config 配置,或使用配置加载方案(如 config.from_object)进行设置。

SQLALCHEMY 的主要配置如下:

配置项 说明 示例
SQLALCHEMY_DATABASE_URI 设置数据库的连接地址 postgresql://root:123456@127.0.0.1:5432/test31
SQLALCHEMY_BINDS 访问多个数据库时,用于设置数据库的连接地址 {"other_db": "sqlite:///other.db"}
SQLALCHEMY_ECHO 是否打印底层执行的 SQL 语句 True
SQLALCHEMY_RECORD_QUERIES 是否记录执行的查询语句,用于慢查询分析,调试模式下自动启动 False
SQLALCHEMY_TRACK_MODIFICATIONS 是否追踪数据库变化(触发钩子函数),会消耗额外的内存 False
SQLALCHEMY_ENGINE_OPTIONS 设置针对 sqlalchemy 本体的配置项 SQLALCHEMY_POOL_SIZE: 连接池最大数量 SQLALCHEMY_POOL_RECYCLE: 单个连接最长生命周期

要从 Flask 中将配置信息加载到 Flask-SQLAlchemy,可以使用 db.init(flask_app) 即可。

应用ORM模型的创建与增删改查

创建模型

要想在项目中使用 ORM,必须创建对应的类,并继承 db.Model,即可实现与对应数据库进行关联。在类中还可以通过 __tablename__ 属性来设置表名,通过 __table_args__ 来为表添加相应的主键、index 索引、unique 索引等约束。

其中关于 ORM 模型的字段类型、索引约束、主键等均可以从 sqlalchemy 包中导入,从这个包导入的类支持所有的数据库,在切换数据库时不需要进行调整,除此之外还可以导入特定数据库特有的结构,例如 Postgres 中特有的 JSONB 数据类型,特有的类型在切换数据库时,需要修改代码进行调整。

一些常见的 SQLAlchemy 数据类型

python 复制代码
from sqlalchemy import (
    Column,              # 用户定义字段
    UUID,                # UUID 类型,例如:123e4567-e89b-12d3-a456-426655440000
    String,              # 字符串,对应数据库的 varchar
    Text,                # 长文本类型,对应数据库的 text
    DateTime,            # 时间类型,对应数据库的 timestamp
    Integer,             # 整型,对应数据库的 int
    Decimal,             # 小数型,对应数据库的 decimal
    PrimaryKeyConstraint, # 创建主键约束
    Index,               # 创建索引
    UniqueConstraint,    # 创建唯一值约束
)
from sqlalchemy.dialects.postgresql import JSONB # Postgres 特有的 JSONB 数据类型,使用二进制存储 JSON,性能更强

完整实例:

python 复制代码
import uuid
from datetime import datetime

from sqlalchemy import (
    Column,
    UUID,
    String,
    DateTime,
    PrimaryKeyConstraint,
    Index,
)
from sqlalchemy.dialects.postgresql import JSONB

from internal.extension.database_extension import db


class App(db.Model):
    """AI应用模型"""
    __tablename__ = "app"
    __table_args__ = (
        PrimaryKeyConstraint("id", name="pk_app_id"),
        Index("idx_app_account_id", "account_id"),
    )
    
    id = Column(UUID, default=uuid.uuid4, nullable=False)
	account_id = Column(UUID, nullable=False)
	name = Column(String(255), default="", nullable=False)
	icon = Column(String(255), default="", nullable=False)
	config = Column(JSONB, default={}, nullable=False)
	updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, nullable=False)
	created_at = Column(DateTime, default=datetime.now, nullable=False) 

创建完模型后,可以通过调用 db.create_all() 在对应的数据库中创建相应的模型表,例如:

python 复制代码
from flask import Flask
from internal.model import App


class Http(Flask):
    def __init__(self):
        # ... 其他初始化代码
        with self.app_context():
            # 确保模型已被导入,这样 db.create_all() 才会识别它
            _ = App()
            # 创建所有定义的模型表
            db.create_all()
        ...

对于这类规律性 + 重复性的代码,可以考虑使用 ChatGPT 来生成,创建 ORM 模型提示词:

对于这类规律性+重复性的代码,可以考虑使用 ChatGPT 来生成,创建 ORM 模型提示词:

角色

你是一个拥有10年经验的资深Python工程师,精通Flask,Flask-SQLAlchemy,Postgres,以及其他Python开发工具,能够为用户提出的需求或者提供的代码段生成指定的完整代码。

技能说明

  • 如果需要实现Flask-SQLAlchemy的ORM类,集成 db.Model 时,从 from internal.extension.database_extension import db 这里导入 db
  • 创建ORM模型时,表名 __tablename__ 及类名全部都是单数;
  • 所有的字段都要添加 nullable=False 代表字段不允许为空;
  • UUID类型的字段添加默认值 default=uuid.uuid4,String类型的字段长度均设置为 String(255),默认值设置为 default=""
  • 所有模型都有 updated_atcreated_at 字段,类型均是 DateTime,其中 updated_at 包含 defaultonupdate,而 created_at 仅包含 default,值全部都是 datetime.now
  • 请给ORM模型添加上 __table_args__ 属性,涵盖 PrimaryKeyConstraint 为主键,所有模型都以 id 为主键,主键的类型为 UUID,如果用户声明其他约束,例如 UniqueConstraintIndex 等时,请按照需求进行添加;
  • 属性的类型全部从 sqlalchemy 包中导入,例如:from sqlalchemy import (Column, UUID, String, DateTime, PrimaryKeyConstraint, UniqueConstraint)
  • uuid.uuid4import uuid 中导入,datetime.nowfrom datetime import datetime 导入;
  • 对于 description 等字段,通过字面意思,可以看出是描述,一般内容比较长,可以使用 Text 类型;
  • 用户如果表名了某个字段类型为 json,则统一设置成 JSONB,并从 from sqlalchemy.dialects.postgresql import JSONB 导入,这是Postgres特有的;
  • 其他的规范请根据你的知识库进行操作,项目使用的数据库是Postgres;

操作实例

bash 复制代码
import uuid
from datetime import datetime

from sqlalchemy import (
    Column,
    UUID,
    String,
    DateTime,
    PrimaryKeyConstraint,
    Index,
)
from sqlalchemy.dialects.postgresql import JSONB

from internal.extension.database_extension import db


class App(db.Model):
    """AI应用模型"""
    __tablename__ = "app"
    __table_args__ = (
        PrimaryKeyConstraint("id", name="pk_app_id"),
        Index("idx_app_account_id", "account_id"),
    )
    
    id = Column(UUID, default=uuid.uuid4, nullable=False)
	account_id = Column(UUID, nullable=False)
	name = Column(String(255), default="", nullable=False)
	icon = Column(String(255), default="", nullable=False)
	config = Column(JSONB, default={}, nullable=False)
	updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, nullable=False)
	created_at = Column(DateTime, default=datetime.now, nullable=False) 
	

注意事项

  • 只处理与生成Python相关的提问,对于其他非相关行业问题,请婉拒回答。
  • 只使用用户使用的语言进行回答,不使用其他语言。
  • 确保回答的针对性和专业性。

用户的需求是:

新增数据

在 Flask-SQLAlchemy 中有两种主要的方式来执行数据库查询:

  1. 使用 db.session.query(Model).xxx这种方法使用 SQLAlchemy 原生查询语法来操作数据库,可以使用 .query 来访问一个表的查询,然后使用 .xxx 来指定你要执行的操作,比如 .filter()、.order_by() 等。这种方法更加灵活,可以用于复杂的查询需求。
  2. 也可以使用 ORM 模型的 query 属性每个继承 db.Model 的模型都有一个 query 属性,可以通过这个属性来执行查询,例如 App.query.filter() 等。

示例:

python 复制代码
# 使用 db.session.query.xxx 的方式
from yourapp import db
from yourapp.models import User

# 查询所有用户,并按照ID升序排列
users = db.session.query(User).order_by(User.id).all()

# 查询用户名为'john'的用户
user = db.session.query(User).filter_by(username='john').first()
python 复制代码
# 使用 ORM 模型的方式
from yourapp.models import User

# 查询所有用户,并按照ID升序排列
users = User.query.order_by(User.id).all()

# 查询用户名为'john'的用户
user = User.query.filter_by(username='john').first()

在 LLMOps 项目中,我们约定无论是单表、多表、简单亦或者复杂的操作,均使用 db.session.query.xxx 的方式进行操作,确保业务逻辑的统一性。

如果想要往表里添加数据,只需依次调用 add(model).commit() 即可,示例:

python 复制代码
def demo():
    # 实例化 Students 模型对象
    user = Students(name='yy', fullname='yoyo')
    # 添加到会话,并用commit提交数据
    db.session.add(user)
    db.session.commit()
    return {
        "code": 0,
        "msg": "create success!"
    }

查询数据

在 Flask-SQLAlchemy 中执行数据查询的方式非常多,示例:

python 复制代码
Model.query.all()           # 获取表内的所有数据
Model.query.first()         # 获取第一个查询结果
Model.query.get(id)         # 根据主键 id 进行查询
Model.query.filter().all()  # 筛选查询,相当于 SQL 中的 WHERE 语句
Model.query.filter_by().all() # 筛选查询,相当于 SQL 中的 WHERE 语句
Model.query.filter().one_or_none() # 筛选查询,相当于 SQL 中的 WHERE + LIMIT 1,只获取一条数据

修改和删除数据

在 Flask-SQLAlchemy 中,无论是更新还是删除,只要使用了 db.session 进行操作都需要调用 commit() 才会将数据同步到实际数据库中。

python 复制代码
# 1.通过调用 update 方法进行更新(支持批量)
Students.query.filter_by(name='yy').update({"fullname": "xx"})

# 2.通过修改模型实例属性进行更新
student = Student.query.first()
student.name = "imooc"
db.session.commit()
python 复制代码
# 1.通过调用 delete 方法删除数据(支持批量)
Student.query.filter_by(name="imooc").delete()

# 2.通过 db.session.delete 进行删除
student = Student.query.first()
db.session.delete(student)
db.session.commit()

重写SQLAlchemy核心类实现自动提交

commit与rollback

对于新增、修改、删除的操作,每次都需要写 db.session.commit() 进行提交,并且在业务逻辑中,如果触发了异常,还需要调用 db.session.rollback() 进行回滚,例如:

python 复制代码
try:
    student = Student.query.first()
    student.name = "imooc"
    db.session.commit()
except Exception as e:
    db.session.rollback()

对于重复性的操作,一般的方式是提供一个工具方法或工具类,所以可以重写 SQLAlchemy 核心类,创建 auto_commit 并使用 contextmanager 装饰该函数。

python 复制代码
from contextlib import contextmanager

from flask_sqlalchemy import SQLAlchemy as _SQLAlchemy


class SQLAlchemy(_SQLAlchemy):
    """重写SQLAlchemy核心,实现数据自动提交"""

    @contextmanager
    def auto_commit(self):
        try:
            yield
            self.session.commit()
        except Exception as e:
            self.session.rollback()
            raise e

@contextmanager 装饰器的原理其实非常简单,它将生成器函数中 yield 之前的部分视为上下文的进入操作,yield 之后的部分视为离开操作。因此,我们可以在 yield 之前对数据进行操作(新增、修改、删除),在 yield 之后执行 self.session.commit() 提交更改。

有了该方法后,对于数据库操作,我们可以这样简化代码:

python 复制代码
with db.auto_commit():
    student = Student.query.first()
    student.name = "imooc"

Flask-Migrate扩展介绍与使用

我们使用 db.create_all() 将 ORM 模型映射到数据库的表中,但是如果新增了 ORM 模型,或者更新了 ORM 模型,使用 db.create_all() 都不会重新创建表或是更新表。

需要先使用 db.drop_all() 删除数据库中的所有表之后再调用 db.create_all() 才能重新创建,但是这样的话,原来表中的数据就都被删除了,这肯定是不行的,于是就有了数据库迁移。

Flask-Migrate 初始化

在开发过程中,随着需求的变化,有可能需要添加或修改表的一些字段,但是原表中的数据不能删除,此时就需要创建新表,并将旧表中的数据迁移至新表中,Flask-Migrate 这个扩展就可以在不破坏数据的情况下更新数据库表的结构,并完成数据从旧表到新表的迁移。

安装命令:

bash 复制代码
pip install flask-migrate

安装后进行初始化即可:

python 复制代码
from flask_migrate import Migrate

migrate = Migrate()
migrate.init_app(app, db, directory="internal/migration")

Flask-Migrate 常见命令

初始化迁移环境

在开始迁移数据之前,需要先使用 init 命令初始化创建一个迁移环境:

bash 复制代码
# 当 flask 的应用入口在项目根目录,且文件名为 app.py,并且实例变量名为 app 时
flask db init

# LLM Ops 项目运行命令
flask --app app.server.app db init

运行命令后,会在 directory 指定的位置创建一个迁移环境,默认位置为 ./migrations。

生成迁移脚本

使用如下命令自动生成迁移脚本:

bash 复制代码
flask --app app.server.app db migrate -m "create_table"

其中 -m "create_table" 是可选的,用于为这次迁移添加一个简单的注释。

在生成的迁移脚本中,会包含两个函数:

  • upgrade():把迁移的改动应用到数据中
  • downgrade():将改动撤销

注意下,在生产环境中一般不使用 Flask-Migrate 迁移,一般人工生成相应的 SQL 执行迁移,如果要使用迁移,一定要仔细检查生成的迁移文件是否符合预期,确认后才可以使用。

更新及回滚数据库

生成迁移脚本后,可以使用 upgrade 命令来更新数据库:

python 复制代码
flask --app app.server.app db upgrade

每一次更新 ORM 模型,都需要执行 migrate 命令生成迁移文件,然后才可以使用 upgrade 命令将更新同步到数据库中。

如果想要回滚数据库,可以使用 downgrade 命令:

python 复制代码
flask --app app.server.app db downgrade

每执行一次命令会向上回滚一个版本,如果想一次性回滚到最原始的版本(即删除所有数据库表),可以使用如下命令:

python 复制代码
flask --app app.server.app db downgrade base

如果想回滚到特定的版本,可以在 downgrade 后带上特定的版本号:

python 复制代码
flask --app app.server.app db downgrade <版本号>
相关推荐
Li emily5 小时前
用Python批量调用外汇接口获取多货币汇率
人工智能·python·api·fastapi
财经资讯数据_灵砚智能5 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年4月30日
人工智能·python·信息可视化·自然语言处理·ai编程
xzal125 小时前
pygame之键盘控制方块移动方法
python·pygame
song8546011345 小时前
MYSQL优化器的主要的优化策略及其示例
数据库·mysql
天才少女爱迪生5 小时前
word格式规范检测+自动修改【python】
python·c#·word
南宫萧幕5 小时前
基于 PSO 的 HEV 能量管理策略:从联合仿真建模到排错实战
开发语言·python·算法·matlab·控制
在下_诸葛5 小时前
langgraph学习笔记
笔记·python·学习·langgraph教程
杨浦老苏5 小时前
基于MongoDB Atlas的博客热榜
数据库·博客·blog·waline
Bert.Cai5 小时前
MySQL RAND()函数详解
数据库·mysql