简体   繁体   English

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

[英]Flask - working outside of application context

I am testing a Flask application and am receiving a "working outside of application context" error.我正在测试 Flask 应用程序并收到“在应用程序上下文之外工作”错误。 My file directory is as follows:我的文件目录如下:

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

My app.py file looks like the following:我的 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)

My users.py file is as follows:我的 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

I run the file in the same directory as app.py using我使用与 app.py 在同一目录中运行该文件

python app.py

But it returns the following error:但它返回以下错误:

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

If I move the contents of the user.py file into the app.py file and change the inheritance from current_app.dbuser.Model to app.dbuser.Model it seems to work fine.如果我将 user.py 文件的内容移动到 app.py 文件中并将继承从 current_app.dbuser.Model 更改为 app.dbuser.Model 它似乎工作正常。 Does anyone know what I am doing wrong?有谁知道我做错了什么?

Flask-Sqlalchemy binds some sqlalchemy concepts like the session, the engine, and the declaritive base to the flask app. Flask-Sqlalchemy 将一些 sqlalchemy 概念(如会话、引擎和声明性基础)绑定到烧瓶应用程序。 This is convenient because you only have one thing to instantiate at your uwsgi entry point (the app object) but a pain when testing- because you have to instantiate the app object.这很方便,因为您在 uwsgi 入口点(应用程序对象)只有一件事要实例化,但在测试时会很痛苦——因为您必须实例化应用程序对象。

EDIT- I am leaving the part about testing below, but I reread your question and realized you're not actually trying to test anything.编辑-我将在下面留下有关测试的部分,但我重新阅读了您的问题并意识到您实际上并没有尝试测试任何东西。

You don't have access to the 'current_app' object at import time (when you are trying to initialize your sqlalchemy models).您在导入时无权访问“current_app”对象(当您尝试初始化 sqlalchemy 模型时)。 Instead, you have to actually import the app object from your app file.相反,您必须从您的应用程序文件中实际导入应用程序对象。 This of course means you have to worry about circular dependencies...这当然意味着您必须担心循环依赖...

I have a method called 'register_routes' that gets called after I initialize the app object that imports models and views files that require access to the app object at import time.我有一个名为“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):
   ...

EDIT- the below discusses this issue with respect to unit testing编辑-下面讨论了关于单元测试的这个问题

Flask-Testing is a project that attempts to solve these problems, and is almost certainly appropriate for a beginner in this area- it provides a test class to inherit from that will set up your flask app before test cases and tear it down after. Flask-Testing 是一个试图解决这些问题的项目,并且几乎可以肯定适合该领域的初学者——它提供了一个可以继承的测试类,它将在测试用例之前设置你的烧瓶应用程序并在之后将其拆除。 (As you come to understand the various globals and what they do you may want to move away from this... but it is very helpful for getting started!) (当您了解各种全局变量以及它们的作用时,您可能希望摆脱这一点……但这对入门非常有帮助!)

If you don't want to do that, you need to create an app and initialize an app context before doing anything with your flask-sqlalchemy models.如果您不想这样做,则需要先创建一个应用程序并初始化一个应用程序上下文,然后再对您的 flask-sqlalchemy 模型进行任何操作。 This may just be这可能只是

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

You will probably want to refresh this in between methods, otherwise global state will leak between test cases.您可能希望在方法之间刷新它,否则全局状态将在测试用例之间泄漏。

Basically, flask uses quite a lot of global variables like current_app , request etc. which only exist when a flask app is instantiated and running and in various states.基本上,flask 使用了很多全局变量,例如current_apprequest等,这些变量仅在 flask 应用程序被实例化并运行并处于各种状态时才存在。

You have used current_app in the definition of the User object which is evaluated as soon as the file is imported by Python.您在 User 对象的定义中使用了current_app ,该对象在 Python 导入文件后立即进行评估。 You need to ensure you only use values like this when an app is already running.您需要确保仅在应用程序已运行时才使用此类值。

You could move the instantiation of the User class until after the app exists, but I think the root problem is why are you using current_app.dbuser.Boolean rather than say sqlalchemy.types.Boolean ?您可以将 User 类的实例化移动到应用程序存在之后,但我认为根本问题是您为什么使用current_app.dbuser.Boolean而不是说sqlalchemy.types.Boolean

I'm not a great expert on flask.ext.sqlalchemy , but my guess is you don't need to load those definitions of things like Column and Boolean from the particular instance of the app you have.我不是flask.ext.sqlalchemy方面的专家,但我猜您不需要从您拥有的应用程序的特定实例中加载诸如ColumnBoolean之类的定义。 Using the static definitions from sqlalchemy would prevent you from having a dependency from the User class to the app.使用sqlalchemy中的静态定义会阻止您从User类中获得对应用程序的依赖。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM