2017-07-04 59 views
4

我正在爲pytest配置一個夾具來創建瓶子應用程序實例。我的應用程序使用Application Factories pattern創建。我處於將其連接到數據庫的階段,並努力理解2種模式之間的差異。db.session.commit是否在Flask-SQLAlchemy中更改應用程序上下文?

# 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:[email protected]: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()運行夾具,想,也許我是在應用程序上下文默認情況下(類似到我在with app_context()中做的第一個燈具)。但如果我刪除它,它會失敗。

+0

我無法重現您的問題。第二個裝置完全失敗,因爲你沒有推送應用程序上下文,而不是因爲你正在添加或刪除'db.session.commit()'。 – davidism

回答

6

第一個問題是,所述第二設置(由testdriven.io)不使用應用程序工廠模式,他們明確地實例化DB和結合該應用(例如,相對於db = SQLAlchemy(app)db = SQLAlchemy()create_app()稍後db.init(app))。如果您使用的是應用程序工廠模式,則在執行db.create_all()後,您會在交互式會話中看到錯誤,保留/刪除db.session.commit()不會有任何幫助。

我有一個想法,你正在使用兩個不同的from project import create_app, db爲每個嘗試和交互式shell使用應用程序工廠模式。

無論如何,你真的提出兩個問題。

1)爲什麼我需要推送應用程序上下文以便在使用Application Factory模式時運行db.create_all()

如果你看一下在SQLAlchemy__init__方法,你會發現,你可以通過一個應用程序,在這種情況下,該應用程序變得綁定到SQLAlchemy的對象,self.app = app。但是,因爲您使用的是應用程序工廠模式,所以即使在運行db.init_app(app)之後,該應用程序也不會顯式綁定。現在看create_all(),它需要一個可選的app,這是你沒有通過,所以當我們到達get_app,我們跳過reference_app,因爲它是None,我們超過current_app,它看着Flask的應用程序上下文(請參閱from flask import current_app) ,如果你還沒有按上下文,這也將是None,最後我們檢查是否有self.app,但這也是None,因爲我們使用應用程序工廠模式,因此application not registered on db instance and no application bound to current context錯誤。

2)爲什麼我需要SQLAlchemy的實例化過程中結合應用程序時運行db.create_all後明確地運行db.session.commit()(例如,db = SQLAlchemy(app))?**

我似乎無法重現此錯誤,我已經添加下面的代碼塊告訴你我在使用什麼,我從testdriven.io網站獲得了這些作品。但是,您不應該需要db.session.commit(),使用應用程序工廠模式和實例化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() 

參考文獻:

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

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

相關問題