记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语句放到后面,完全不需要包装一层工厂函数,就可以解决这个问题。


相关推荐
Karoku06625 分钟前
【企业级分布式系统】ELK优化
运维·服务器·数据库·elk·elasticsearch
drebander29 分钟前
使用 Java Stream 优雅实现List 转化为Map<key,Map<key,value>>
java·python·list
威威猫的栗子1 小时前
Python Turtle召唤童年:喜羊羊与灰太狼之懒羊羊绘画
开发语言·python
墨染风华不染尘1 小时前
python之开发笔记
开发语言·笔记·python
小技与小术2 小时前
数据库表设计范式
数据库·mysql
安迁岚2 小时前
【SQL Server】华中农业大学空间数据库实验报告 实验三 数据操作
运维·服务器·数据库·sql·mysql
安迁岚2 小时前
【SQL Server】华中农业大学空间数据库实验报告 实验九 触发器
数据库·sql·mysql·oracle·实验报告
Dxy12393102162 小时前
python bmp图片转jpg
python
Loganer2 小时前
MongoDB分片集群搭建
数据库·mongodb
麦麦大数据2 小时前
Python棉花病虫害图谱系统CNN识别+AI问答知识neo4j vue+flask深度学习神经网络可视化
人工智能·python·深度学习