我正在为pytest配置一个fixture来创建一个烧瓶app实例。我的应用是使用Application Factories pattern创建的。我正处于将其连接到数据库的阶段,并且很难理解2种模式之间的区别。
# project/__init__.py
import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app_settings = os.getenv('APP_SETTINGS')
app.config.from_object(app_settings)
db.init_app(app)
[blueprint code]
return app
在我的灯具中,我想我理解需要:
db.create_all()
:创建我的表格db.drop_all()
:测试后清理数据库db.session.remove()
:在测试中频繁访问数据库时,避免使用postgres上的一些奇怪锁定第一个设置(受Miguel Grinberg book启发)对我有意义:
import pytest
from project import create_app, db
@pytest.fixture
def app():
app = create_app()
with app.app_context():
db.create_all()
yield app
db.session.remove()
db.drop_all()
它还匹配我在交互式会话中获得的行为,我需要激活/推送app_context
以绑定数据库:
Python 3.6.1 (default, Jun 21 2017, 18:45:41)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from project import create_app, db
>>> app = create_app()
>>> db
<SQLAlchemy engine=None>
>>> app_ctx = app.app_context()
>>> app_ctx.push()
>>> db.create_all()
>>> db
<SQLAlchemy engine='postgres://postgres:postgres@users-db:5432/users_dev'>
第二个设置(受testdriven.io启发)也适用于pytest,但我不知道原因:
import pytest
from project import create_app, db
@pytest.fixture
def app():
app = create_app()
db.create_all()
db.session.commit() # fail when this is removed
yield app
db.session.remove()
db.drop_all()
实际上,如果我尝试在交互式会话中执行相同的操作,我会收到错误:
Python 3.6.1 (default, Jun 21 2017, 18:45:41)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from project import create_app, db
>>> app = create_app()
>>> db.create_all()
[...] timeError: application not registered on db instance and no application bound to current context
我尝试在没有db.session.commit()
的情况下运行灯具,认为可能默认情况下我在app环境中(类似于我在第一个灯具中对with app_context()
所做的操作)。但如果我删除它就会失败。
答案 0 :(得分:6)
第一个问题是第二个设置(由testdriven.io
)没有使用应用工厂模式,他们明确地实例化数据库并绑定应用程序(例如,db = SQLAlchemy(app)
与{ {1}}以及db = SQLAlchemy()
中的db.init(app)
以后的create_app()
。如果您使用的是应用程序工厂模式,那么在执行db.create_all()
后,您将在交互式会话中看到错误,保持/删除db.session.commit()
无论如何都不会帮助。< / p>
我清楚地知道,您为每次尝试使用两个不同的from project import create_app, db
,并使用Application Factory模式进行交互式shell。
无论如何,你真的要问两个问题。
1)为什么在使用Application Factory模式时需要推送应用程序上下文才能运行db.create_all()
?
如果您查看__init__
中的SQLAlchemy
方法,您会注意到您可以通过应用程序,在这种情况下,该应用程序将绑定到SQLAlchemy对象{{1} 1}}。但是,由于您正在使用Application Factory模式,因此即使在您运行self.app = app
之后,该应用也从未明确绑定。现在查看db.init_app(app)
,它需要一个可选的create_all()
,您无法通过,所以当我们到达app
时,我们跳过get_app
,因为它是reference_app
,我们查看了None
,它会查看Flask的应用程序上下文(请参阅current_app
),如果您还没有推送上下文,这也将是from flask import current_app
,最后我们检查是否有None
,但这也是self.app
因为我们正在使用应用工厂模式,因此None
错误。< / p>
2)为什么在SQLAlchemy实例化期间绑定应用程序(例如application not registered on db instance and no application bound to current context
)时运行db.session.commit()
后需要显式运行db.create_all
?**
我似乎无法重现此错误,我在下面添加了一个代码块来向您展示我正在使用的内容,我从db = SQLAlchemy(app)
网站获得了这些内容。但是,您不应该testdriven.io
,使用Application Factory模式和使用应用程序实例化SQLAlchemy(例如db.session.commit()
)之间的唯一区别就是前者与您的对比要么需要传递db = SQLAlchemy(app)
中的应用程序,要么推送应用程序上下文。
create_all(app)
参考文献: