2016-03-25 21 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 

但它返回以下错误:如果我移动user.py文件的内容到app.py文件并更改继承从current_app.dbuser.Model到app.dbuser.Model似乎

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 

工作正常。有谁知道我做错了什么?

回答

-1

基本上,烧瓶使用了相当多的全局变量,如current_app,request等,只有当烧瓶应用程序被实例化并运行并处于各种状态时才存在。

您已经在User对象的定义中使用了current_app,该对象在Python导入文件后立即进行评估。您需要确保在应用程序已经运行时只使用这样的值。

您可以移动User类的实例化,直到应用程序存在之后,但我认为根本问题是您为什么要使用current_app.dbuser.Boolean而不是说sqlalchemy.types.Boolean

我不是flask.ext.sqlalchemy的专家,但我的猜测是你不需要从你的应用程序的特定实例加载诸如ColumnBoolean之类的定义。使用来自sqlalchemy的静态定义可以防止从User类到应用程序的依赖关系。

+0

这不是准确的。 flask-sqlalchemy为模型设置全局数据库上下文以及像会话这些sqlalchemy定义所需的东西。 –

+0

噢,正如我所说的,我不熟悉'flask.ext.sqlalchemy'。我想我几乎在那里说,虽然它可能会绑定到'current_app'你不想在导入时像那样访问它。 –

+0

是的,这真的是问题所在,但建议的修复会让事情变得更糟:) –

1

Flask-Sqlalchemy将一些sqlalchemy概念(如会话,引擎和声明基础)绑定到烧瓶应用程序。这很方便,因为您只需在uwsgi入口点(应用程序对象)实例化一件东西,但在测试时会很痛苦,因为您必须实例化应用程序对象。

编辑 - 我正在离开下面的测试部分,但我重读了你的问题,并意识到你实际上并没有试图测试任何东西。

您在导入时无法访问'current_app'对象(当您尝试初始化sqlalchemy模型时)。相反,您必须从应用程序文件中实际导入应用程序对象。这当然意味着你不得不担心循环依赖...

我有一个名为'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): 
    ... 

编辑 - 下面讨论关于这一问题的单元测试

瓶测试是试图解决这些问题的项目,几乎可以肯定是适合于初学者这个区域 - 它提供了一个测试类来继承这个测试类,它将在测试用例之前建立你的应用程序,并在之后将其关闭。 (当你开始了解各种全局变量以及它们所做的工作时,你可能想要摆脱这种情况......但这对于入门非常有帮助!)

如果你不想这么做,你需要在使用flask-sqlalchemy模型做任何事情之前创建应用程序并初始化应用程序上下文。这可能只是

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

你可能会想方法之间刷新这一点,否则,全球国家将测试用例之间泄漏。

相关问题