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

xav*_*xav 4 python pytest flask flask-sqlalchemy

我正在为pytest配置一个fixture来创建一个烧瓶app实例.我的应用程序是使用Application Factories模式创建的.我正处于将其连接到数据库的阶段,并且很难理解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
Run Code Online (Sandbox Code Playgroud)

在我的夹具中,我想我理解需要:

  • db.create_all() 在安装过程中:创建我的表格
  • db.drop_all() 拆解期间:测试后清理数据库
  • db.session.remove() 在拆解过程中:在测试中频繁访问数据库时,请避免使用postgres上的一些奇怪锁定

第一个设置(灵感来自Miguel Grinberg的书)对我有意义:

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()
Run Code Online (Sandbox Code Playgroud)

它还匹配我在交互式会话中获得的行为,我需要激活/推送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'>
Run Code Online (Sandbox Code Playgroud)

第二个设置(灵感来自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()
Run Code Online (Sandbox Code Playgroud)

实际上,如果我尝试在交互式会话中执行相同操作,则会收到错误消息:

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
Run Code Online (Sandbox Code Playgroud)

我试图在没有的情况下运行灯具db.session.commit(),认为我可能默认在应用程序上下文中(类似于我with app_context()在第一个灯具中执行的操作).但是如果我删除它就会失败.

jac*_*kar 7

第一个问题是,第二设置(通过testdriven.io)不使用应用程序工厂模式,是通过明确实例数据库,并结合应用(例如,db = SQLAlchemy(app)db = SQLAlchemy()和以后db.init(app)create_app()).如果您使用的是Application Factory模式,则会在执行后获得您在交互式会话中看到的错误db.create_all(),保留/删除db.session.commit()无论如何都无济于事.

我有一个暗示,你from project import create_app, db为每次尝试使用两种不同的方法,并使用Application Factory模式进行交互式shell.

无论如何,你真的在​​问两个问题.

1)为什么db.create_all()在使用Application Factory模式时需要推送应用程序上下文才能运行?

如果你在寻找__init__方法SQLAlchemy,你会发现,你可以通过一个应用程序,在这种情况下,该应用程序变得绑定到SQLAlchemy的对象self.app = app.但是,因为您正在使用Application Factory模式,所以即使在您运行之后,该应用也从未明确绑定db.init_app(app).现在看一下create_all(),它需要一个可选的app,你没有通过,所以当我们到达时get_app,我们跳过reference_appNone,因为它是,我们过去current_app,看看Flask的应用程序上下文(请参阅参考资料from flask import current_app),如果你没有推上下文,这也将是None,最后我们检查是否有self.app,但这也是None因为我们正在使用Application Factory模式,因此application not registered on db instance and no application bound to current context错误.

2)为什么在SQLAlchemy实例化期间绑定应用程序时运行db.session.commit()后需要显式运行db.create_all(例如db = SQLAlchemy(app))?**

我似乎无法重现这个错误,我在下面添加了一个代码块来向您展示我正在使用的内容,我从testdriven.io网站上获得了这些内容.但是,您不应该这样做db.session.commit(),使用Application Factory模式和使用应用程序实例化SQLAlchemy(例如db = SQLAlchemy(app))之间的唯一区别是,使用前者,您需要传入应用程序create_all(app)或推送应用程序上下文.

import pytest
import os
import datetime
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

db = SQLAlchemy(app)


@pytest.fixture
def app():
    app = create_app()
    db.create_all()
    # db.session.commit()  # Try this with and without this line
    yield app
    db.session.remove()
    db.drop_all()
Run Code Online (Sandbox Code Playgroud)

参考文献:

https://github.com/mitsuhiko/flask-sqlalchemy/blob/d71afea650e0186348d81f02cca5181ed7c466e9/flask_sqlalchemy/ 初始化的.py

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