2016-03-25 29 views
0

Flaskアプリケーションをテストしていて、「アプリケーションコンテキスト外で動作しています」というエラーが表示されています。次のように私のファイルのディレクトリは次のとおりです。Flask - アプリケーションコンテキスト外での作業

api 
    app.py 
    __init__.py 
    models 
     __init__.py 
     user.py 
    resources 
     __init__.py 
     deals.py 
     stores.py 
    common 
     __init__.py 
     calculations.py 
     decorators.py 

私app.pyファイルには、次のようになります。

import os 
from flask import Flask, jsonify, url_for, redirect, request, g, current_app 
from flask_pymongo import PyMongo 
from flask_restful import Api, Resource 
from flask_httpauth import HTTPTokenAuth 
from flask.ext.sqlalchemy import SQLAlchemy 
from flask.ext.httpauth import HTTPBasicAuth 

from resources.deals import Deals 
from resources.stores import Stores 

from models.user import User 

USERDBFILE=os.path.join(os.path.join(os.path.dirname(os.path.realpath(__file__)),'database'),'db.sqlite') 

#Deals database 
app = Flask(__name__) 
app.config["MONGO_DBNAME"] = "database" 
mongo = PyMongo(app,config_prefix='MONGO') 
app.db = mongo 

#User database 
app.config['SECRET_KEY'] = 'SECRET KEY' 
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite' 
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True 
app.dbuser = SQLAlchemy(app) 

#App url 
app.APP_URL = "http://127.0.0.1:5000" 

#Setup authorization 
auth = HTTPTokenAuth(scheme='Token') 

#Setup the app 
api = Api(app) 
api.add_resource(Deals, '/deals', '/Deals/<string:type>/<string:id>',endpoint="dealType") 
api.add_resource(Stores, '/stores', '/Stores/<string:type>/<string:id>',endpoint="type") 

if __name__ == "__main__": 
    if not os.path.exists(USERDBFILE): 
     app.dbuser.create_all() 
    app.run(debug=True) 

次のように私のusers.pyファイルは次のとおりです。

from flask import current_app 
import os 
from flask import Flask, abort, request, jsonify, g, url_for 
from flask.ext.sqlalchemy import SQLAlchemy 
from flask.ext.httpauth import HTTPBasicAuth 
from passlib.apps import custom_app_context as pwd_context 
from itsdangerous import (TimedJSONWebSignatureSerializer 
          as Serializer, BadSignature, SignatureExpired) 

class User(current_app.dbuser.Model): 
    __tablename__ = 'user_api' 
    id = current_app.dbuser.Column(current_app.dbuser.Integer,primary_key=True) 
    date_created = current_app.dbuser.Column(current_app.dbuser.DateTime,default=current_app.dbuser.func.current_timestamp()) 
    date_modified = current_app.dbuser.Column(current_app.dbuser.DateTime,default=current_app.dbuser.func.current_timestamp(), 
             onupdate=current_app.dbuser.func.current_timestamp()) 
    # User Name 
    name = current_app.dbuser.Column(current_app.dbuser.String(128),nullable=False) 
    # Identification Data: email & password 
    email = current_app.dbuser.Column(current_app.dbuser.String(128),nullable=False,unique=True) 
    password = current_app.dbuser.Column(current_app.dbuser.String(192),nullable=False) 
    company = current_app.dbuser.Column(current_app.dbuser.String(128),nullable=False,unique=True) 
    # Authorization Data: role & status 
    role = current_app.dbuser.Column(current_app.dbuser.String(32),nullable=False,default='user') 
    status = current_app.dbuser.Column(current_app.dbuser.Boolean,nullable=False,default=True) 
    hourly_limit = current_app.dbuser.Column(current_app.dbuser.Integer,nullable=False,default=100) 
    daily_limit = current_app.dbuser.Column(current_app.dbuser.Integer,nullable=False,default=2400) 
    monthly_limit = current_app.dbuser.Column(current_app.dbuser.Integer,nullable=False,default=2400) 
    admin = current_app.dbuser.Column(current_app.dbuser.Boolean,nullable=False,default=True) 

    def hash_password(self, password): 
     self.password_hash = pwd_context.encrypt(password) 

    def verify_password(self, password): 
     return pwd_context.verify(password, self.password_hash) 

    def generate_auth_token(self, expiration=600): 
     s = Serializer(current_app.config['SECRET_KEY'], expires_in=expiration) 
     return s.dumps({'id': self.id}) 

    @staticmethod 
    def verify_auth_token(token): 
     s = Serializer(current_app.config['SECRET_KEY']) 
     try: 
      data = s.loads(token) 
     except SignatureExpired: 
      return None # valid token, but expired 
     except BadSignature: 
      return None # invalid token 
     user = User.query.get(data['id']) 
     return user 

私は実行しますapp.pyと同じディレクトリにあるファイル

python app.py 

しかし、それは次のエラーが返されます。

File "app.py", line 13, in <module> 
    from models.user import User 
    File "/Users/toby/api/api/models/user.py", line 10, in <module> 
    class User(current_app.dbuser.Model): 
    File "/Users/toby/api/venv/lib/python3.4/site-packages/werkzeug/local.py", line 343, in __getattr__ 
    return getattr(self._get_current_object(), name) 
    File "/Users/toby/api/venv/lib/python3.4/site-packages/werkzeug/local.py", line 302, in _get_current_object 
    return self.__local() 
    File "/Users/toby/api/venv/lib/python3.4/site-packages/flask/globals.py", line 34, in _find_app 
    raise RuntimeError('working outside of application context') 
RuntimeError: working outside of application context 

私はapp.pyファイルにuser.pyファイルの内容を移動し、app.dbuser.Modelするcurrent_app.dbuser.Modelからの継承を変更した場合、それはそうですうまく動作する。誰かが私が間違っていることを知っていますか?

答えて

-1

フラスコアプリはインスタンス化され実行されているときにのみ存在する、current_apprequestなどのような大量のグローバル変数を基本的に使用します。

ユーザオブジェクトの定義にcurrent_appを使用しました。これは、ファイルがPythonによってインポートされるとすぐに評価されます。アプリがすでに実行中の場合にのみ、このような値を使用するようにする必要があります。

アプリが存在するまでUserクラスのインスタンスを移動できますが、sqlalchemy.types.Booleanというより、current_app.dbuser.Booleanを使用している理由は根本的な問題だと思いますか?

私はflask.ext.sqlalchemyに大きな専門家ではないんだけど、私の推測では、あなたが持っているアプリの特定のインスタンスからColumnBooleanのようなもののそれらの定義をロードする必要はありませんです。 sqlalchemyの静的な定義を使用すると、Userクラスからそのアプリケーションへの依存を防ぐことができます。

+0

これは正確ではありません。フラスコSQLAlchemyのはSQLAlchemyの定義のために必要とされるセッション、のようなものと一緒にモデルのグローバルデシベルコンテキストを設定します。 –

+0

ああ、私が言ったように、私は 'flask.ext.sqlalchemy'に慣れていません。私はそれはあなたがインポート時にそのようにアクセスさせたくないcurrent_app' 'にバインドされてもよいと言って、ほとんどがあったとします。 –

+0

はい、それは本当に問題でしたが、提案された修正は状況を悪化させました:) –

1

Flask-Sqlalchemyは、セッション、エンジン、宣言ベースなどのsqlalchemyの概念をフラスコアプリにバインドします。これは、uwsgiエントリポイント(アプリケーションオブジェクト)でインスタンス化することが1つしかないため、アプリケーションオブジェクトをインスタンス化する必要があるため、テスト時に痛みがあるので便利です。

EDIT-私は以下のテストについての部分を残していますが、あなたの質問を再度読み、何かを実際にテストしようとしていないことを認識しました。

インポート時に(「sqlalchemy」モデルを初期化しようとしているとき)、「current_app」オブジェクトにアクセスすることはできません。その代わりに、アプリファイルからアプリオブジェクトを実際にインポートする必要があります。これはもちろん、循環依存性について心配する必要があることを意味します...

私はモデルをインポートし、インポート時にアプリケーションオブジェクトへのアクセスを必要とするファイルを表示するアプリケーションオブジェクトを初期化した後に呼び出される 'register_routes'というメソッドを持っています時間。

#at the bottom of app.py 
def register_models(app): 
    from models import User 

register_models(app) 
# in models.py 
from app import app 

class User(app.dbuser.Model): 
    ... 

編集 - 以下のユニットテスト

フラスコ-テストは、これらの問題を解決しようとするプロジェクトで、ほぼ確実で初心者に適しているに関して、この問題を議論テストケースの前にフラスコのアプリを設定し、それを破棄するテストクラスを継承するテストクラスを提供します。 (あなたは、さまざまなグローバルと何彼らはあなたがここから離れて移動することもできますを理解するために来る...それが始めるために非常に有用であるとして!)あなたがそれを行うにはしたくない場合は

、次のものが必要あなたのフラスコ - sqlalchemyモデルで何かをする前にアプリを作成し、アプリのコンテキストを初期化する。これは単に、あなたはおそらく、メソッドの間、これをリフレッシュしたいと思うでしょう

app = myapp.create() 
with app.test_request_context(): 
    # do some testing... 

かもしれそうでないグローバル状態は、テストケースの間でリークが発生します。

関連する問題