记Flask-Migrate迁移数据库失败的两个Bug——详解循环导入问题

文章目录

    • Flask-Migrate迁移数据库失败的两个Bug
      • [1、找不到数据库:Unknown database '***'](#1、找不到数据库:Unknown database '***')
      • [2、迁移后没有效果:No changes in schema detected.](#2、迁移后没有效果:No changes in schema detected.)

Flask-Migrate迁移数据库失败的两个Bug

1、找不到数据库:Unknown database '***'

若还没有创建数据库,该迁移工具不会自动创建。你可以使用SQL命令手动创建一个数据库:

sql 复制代码
create database <数据库名称>

2、迁移后没有效果:No changes in schema detected.

我的情况长话短说,就是创建的数据模型类没有注册到程序实例app,解决方案是使用工厂函数。细说如下:

在项目的主目录下,有两个文件如下所示。我在app.py中创建了程序实例,在models.py中定义了数据模型类。flask 会自动尝试在名为app.py的文件中寻找程序实例,但不会管models.py文件,而我没有在app.py中导入import models,这样它就只是一个孤立的文件,和不存在没啥区别。

复制代码
- app.py
- models.py

解决方案:

那我直接再app.py文件的头部加一句import models不就行了?

  • /app.py
python 复制代码
# import models
# 创建数据库和程序实例
db = SQLAlchemy()
migrate = Migrate()

app = Flask(__name__)

# 注册数据库
db.init_app(app)
migrate.init_app(app, db)
  • /models.py
python 复制代码
from app import db

class UserModel(db.Model):
    ...

答案是不行。因为如果这样的话,from app import db会和import models构成循环导入,导致程序报错。

不过我观察到,有的项目中创建程序实例采用了工厂函数 形式,同时并没有发生循环导入的问题。即把app实例的创建过程代码,从主流程转移到一个函数中去,代码如下所示:

工厂函数 :即返回值是一个可调用对象的函数。

python 复制代码
# 创建数据库实例
db = SQLAlchemy()
migrate = Migrate()

# 工厂函数,返回实例对象
def create_app():
    app = Flask(__name__)
    ...
    db.init_app(app)
	migrate.init_app(app, db)
    # 导入数据模型
    import models
    
    return app

app = create_app()

循环导入的产生机制:

改用工厂函数,是我看了别人的代码后,胡乱之下做的一个尝试,它确实成功解决了问题。可是,我们不免心生疑惑:

没道理啊,为什么工厂函数就可以,我直接导入就不可以呢?

这就不得不仔细思考:"循环导入"这一问题发生的具体条件是什么?只是简单的"A中导入了B,而在B中也导入了A"吗?此时不妨回想一下,Python是一门解释性语言,代码是一行一行地执行的。

而在models.py遇到导入语句from app import db时,是怎样的机制呢?此时会跳转到app.py,一行一行地执行其中的代码,直到找到对象db为止,然后返回继续执行原文件models.py中的代码。

关于此机制我们不妨验证一下,在同一目录下创建两个文件a.pyb.py如下。运行结果中输出了"hello""world",却没有输出"python",说明在完成 f 函数的定义后,a.py的执行就停下来了,继续执行b.py中的代码。

  • /a.py
python 复制代码
print('hello')
def f():
    print('world')
print('python')
  • /b.py
python 复制代码
from a import f
f()
  • python b.py命令的执行结果:

    hello
    world

所以,前面发生循环导入的核心问题,其实只是因为**app.py中的import models语句放在了创建数据库实例的db = SQLAlchemy()语句之前。** 我们只需要将import models语句放到后面,完全不需要包装一层工厂函数,就可以解决这个问题。


相关推荐
JOJO数据科学1 分钟前
DbGate Electron 鸿蒙 PC 适配全记录:从桌面数据库工具到 OpenHarmony HAP
数据库·electron·harmonyos
初圣魔门首席弟子3 分钟前
AI Agent 核心原理:工具调用(Function Calling)完整工作流程详解
前端·数据库·人工智能
半夜修仙3 分钟前
延迟队列的介绍及常见问题
java·数据库·中间件·rabbitmq
herinspace6 分钟前
管家婆云辉煌开单优化
服务器·数据库·电脑·管家婆软件·财务软件
Attachment George7 分钟前
山东大学软件学院-项目实训-个人开发日志(十):材料问答链路开发——文档解析、OCR兜底与持续追问完善
python·ai·langchain·kotlin·rag
码云骑士8 分钟前
24-Django请求全链路-WSGI到数据库响应的完整旅程
数据库·python·django
霖霖总总10 分钟前
[MongoDB小技巧09]深入浅出 MongoDB 逻辑运算符:$and、$or、$nor、$not 原理与实战
数据库·mongodb
小丶舟11 分钟前
MiMo Code实测:5场景对标Claude Code,3个踩坑与选型指南
数据库·人工智能·数据挖掘
z_鑫14 分钟前
深入理解MyBatis:collection集合封装的底层原理与实现细节
java·开发语言·数据库·spring boot·mybatis
贺国亚16 分钟前
06-奢侈零售VIP-Clienteling-Agent
开发语言·python·零售