Skip to main content
 首页 » 编程设计

python之Flask之在应用程序上下文之外工作

2025年05月04日10sky-heaven

我正在测试 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 将 session 、引擎和声明性基础等一些 sqlalchemy 概念绑定(bind)到 Flask 应用程序。这很方便,因为您只有一件事要在您的 uwsgi 入口点(app 对象)实例化,但在测试时会很痛苦——因为您必须实例化 app 对象。

编辑 - 我将离开下面关于测试的部分,但我重新阅读了你的问题并意识到你实际上并没有尝试测试任何东西。

您在导入时无权访问“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-Testing 是一个试图解决这些问题的项目,几乎肯定适合该领域的初学者 - 它提供了一个测试类继承,将在测试用例之前设置您的 Flask 应用程序并将其拆除后。 (当您开始了解各种全局变量以及它们的作用时,您可能想远离这个......但这对入门非常有帮助!)

如果您不想这样做,则需要先创建一个应用程序并初始化一个应用程序上下文,然后再对您的 flask-sqlalchemy 模型执行任何操作。这可能只是

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

您可能希望在方法之间刷新它,否则全局状态将在测试用例之间泄漏。