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