博主在用Flask开发的时候,需要实现一个功能:定时写数据到数据库。之前采用了一个笨方法,单独写一个Python脚本文件,并且数据库表对象还要重新定义,如果Flask那边修改了数据表对象,这边的脚本也需要就改数据表对象,非常麻烦。我于是就想能不能直接用Flask里的数据表对象。可是每次在脚本里调用数据表对象都报错,加上定时任务是用了线程机制,还涉及线程安全问题等等,任务艰巨啊。但是,最后我找到了下面这篇文章,终于明白该怎么做了。
原文链接:http://piotr.banaszkiewicz.org/blog/2012/06/29/flask-sqlalchemy-init_app/
下面是翻译。
我最近正在重构我的Flask应用,并且其中一个最重要的改变是我用到了应用工厂模式。你可以发现很多不同(但类似)使用SQLAlchemy的语法结构。这里是其中一个最常见的:
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
db.init_app(app)
db.create_all()
return app
然而,你应该偶然发现这么一个异常:
RuntimeError: application not registered on db instance and no application bound to current context
也许在你的测试用例中,使用db.create_all()
期间。
文档并没有对此问题有清楚的描述。经过漫长的寻找,我终于发现有三个办法可以解决此问题。
创建你的db
对象,并作为app
的一个参数:
def create_app():
app = Flask(__name__)
db = SQLAlchemy(app)
db.create_all()
return app
缺点是你没有一个全局的db
对象,并且导致你的蓝本无法工作。你将无法容易的创建models。
(顺便说:设置global db
加上db = SQLAlchemy(app)
,对我来说没有效果)
给db.app
分配创建好的应用:
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
db.init_app()
db.app = app
db.create_all()
return app
这个方法的缺点是:你可能会破坏db
内部源码。你不想这么做对吧?
博主备注:我是用这个办法解决了在其它脚本调用
db
数据库表对象的问题。并且实际上这并不会破坏内部源码。可看源码验证。
在应用上下文里面调用db.create_all()
(这就是我们问题的所在)
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
db.init_app(app)
with app.test_request_context():
db.create_all()
return app
缺点是:这是test_request_context
(test!)。但是,毕竟,它对我来说很有效果,我的所有测试用例都通过了!
以上三个方法选择你喜欢的用就行。
如果你遇到了问题,并且用了不同的方法解决,请评论让我知道。
你需要在db.create_all
发生之前导入models本身:
with app.test_request_context():
from application.models import Post
db.create_all()
那么保持你的SQLAlchemy
对象实例放在不同的文件里,以避免不会循环导入,是一个很好的主意:
# application/database.py
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
# application factory
from application.database import db
def create_app():
...
db.init_app(app)
...
# application/models.py
from application.database import db
class Post(db.Model):
...
以上是翻译全文,后来我在查看db.init_app()
源码的时候,找到了get_app
函数,发现里面抛出的异常和译文描述的是一样的信息。说明异常是在这里抛出。
def get_app(self, reference_app=None):
"""Helper method that implements the logic to look up an application.
"""
if reference_app is not None:
return reference_app
if self.app is not None:
return self.app
ctx = connection_stack.top
if ctx is not None:
return ctx.app
raise RuntimeError('application not registered on db '
'instance and no application bound '
'to current context')
说明如果reference_app
、self.app
或ctx
都没有配置好,则会抛出异常!