db.session.com是否在Flask-SQLAlchemy中更改应用程序上下文?

时间:2017-07-04 10:28:55

标签: python flask flask-sqlalchemy pytest

我正在为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()所做的操作)。但如果我删除它就会失败。

1 个答案:

答案 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)

参考文献:

https://github.com/mitsuhiko/flask-sqlalchemy/blob/d71afea650e0186348d81f02cca5181ed7c466e9/flask_sqlalchemy/init.py

http://flask-sqlalchemy.pocoo.org/2.1/contexts/