【问题标题】:Flask - working outside of application contextFlask - 在应用程序上下文之外工作
【发布时间】:2016-07-12 19:41:48
【问题描述】:

我正在测试 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 它似乎工作正常。有谁知道我做错了什么?

【问题讨论】:

    标签: python flask flask-sqlalchemy flask-restful


    【解决方案1】:

    Flask-Sqlalchemy 将一些 sqlalchemy 概念(如会话、引擎和声明性基础)绑定到烧瓶应用程序。这很方便,因为您只需在 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-sqlalchemy 模型进行任何操作。这可能只是

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

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

    【讨论】:

      【解决方案2】:

      基本上,flask 使用了很多全局变量,例如 current_apprequest 等,这些变量仅在 flask 应用程序被实例化并运行并处于各种状态时才存在。

      您在用户对象的定义中使用了current_app,该对象在 Python 导入文件后立即进行评估。您需要确保仅在应用已运行时才使用此类值。

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

      我不是flask.ext.sqlalchemy 方面的专家,但我猜您不需要从您拥有的应用程序的特定实例中加载诸如ColumnBoolean 之类的定义。使用来自 sqlalchemy 的静态定义会阻止您从 User 类对应用程序的依赖。

      【讨论】:

      • 这不准确。 flask-sqlalchemy 为模型设置全局数据库上下文以及会话之类的东西,这是 sqlalchemy 定义所必需的。
      • 哦,好吧,正如我所说,我不熟悉flask.ext.sqlalchemy。我想我几乎要说虽然它可能绑定到current_app,但您不想在导入时那样访问它。
      • 是的,这确实是问题所在,但建议的修复会使事情变得更糟:)
      猜你喜欢
      • 2016-03-11
      • 2020-10-13
      • 1970-01-01
      • 2022-10-06
      • 2021-09-17
      • 1970-01-01
      • 1970-01-01
      • 2015-10-05
      • 2018-11-08
      相关资源
      最近更新 更多