我正在测试 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
如果我将 user.py 文件的内容移至 app.py 文件并将继承从 current_app.dbuser.Model 更改为 app.dbuser.Model ,它似乎工作正常。有谁知道我做错了什么?
Flask-Sqlalchemy 将一些 sqlalchemy 概念(例如会话、引擎和声明性基础)绑定到 Flask 应用程序。这很方便,因为您在 uwsgi 入口点只需实例化一个东西(应用程序对象),但在测试时会很痛苦 - 因为您必须实例化应用程序对象。
编辑-我将在下面留下有关测试的部分,但我重新阅读了你的问题并意识到你实际上并没有尝试测试任何东西。
您在导入时(当您尝试初始化 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):
...
编辑-下面讨论有关单元测试的这个问题
Flask-Testing 是一个尝试解决这些问题的项目,并且几乎肯定适合该领域的初学者 - 它提供了一个可以继承的测试类,它将在测试用例之前设置您的 Flask 应用程序并在测试用例之后将其拆除。 (当您逐渐了解各种全局变量及其作用时,您可能想要摆脱这个......但这对于入门非常有帮助!)
如果您不想这样做,则需要在对 Flask-sqlalchemy 模型执行任何操作之前创建一个应用程序并初始化应用程序上下文。这可能只是
app = myapp.create()
with app.test_request_context():
# do some testing...
您可能希望在方法之间刷新它,否则全局状态将在测试用例之间泄漏。
基本上,Flask 使用了相当多的全局变量,如
current_app
、request
等,这些变量仅在 Flask 应用程序实例化并运行并处于各种状态时才存在。
您在 User 对象的定义中使用了
current_app
,该对象在 Python 导入文件后立即进行评估。您需要确保仅在应用程序已运行时使用此类值。
您可以将 User 类的实例化移动到应用程序存在之后,但我认为根本问题是您为什么使用
current_app.dbuser.Boolean
而不是说 sqlalchemy.types.Boolean
?
我不是
flask.ext.sqlalchemy
方面的专家,但我的猜测是您不需要从您拥有的应用程序的特定实例中加载 Column
和 Boolean
等内容的定义。使用 sqlalchemy
中的静态定义将阻止您从 User
类到应用程序的依赖关系。