Flask-Migrate迁移数据库失败的两个Bug
1、找不到数据库:Unknown database ‘***’
若还没有创建数据库,该迁移工具不会自动创建。你可以使用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
# import models # 创建数据库和程序实例 db = SQLAlchemy() migrate = Migrate() app = Flask(__name__) # 注册数据库 db.init_app(app) migrate.init_app(app, db)
- /models.py
from app import db class UserModel(db.Model): ...
不过我观察到,有的项目中创建程序实例采用了工厂函数形式,同时并没有发生循环导入的问题。即把app实例的创建过程代码,从主流程转移到一个函数中去,代码如下所示:
工厂函数:即返回值是一个可调用对象的函数。
# 创建数据库实例 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.py和b.py如下。运行结果中输出了"hello"和"world",却没有输出"python",说明在完成 f 函数的定义后,a.py的执行就停下来了,继续执行b.py中的代码。
- /a.py
print('hello') def f(): print('world') print('python')
- /b.py
from a import f f()
python b.py
命令的执行结果:
hello world
所以,前面发生循环导入的核心问题,其实只是因为**app.py
中的import models
语句放在了创建数据库实例的db = SQLAlchemy()
语句之前。** 我们只需要将import models
语句放到后面,完全不需要包装一层工厂函数,就可以解决这个问题。