运行时错误:在应用程序上下文之外工作

84

app.py

from flask import Flask, render_template, request,jsonify,json,g
import mysql.connector

app = Flask(__name__)

class TestMySQL():
    @app.before_request
    def before_request():
        try:
            g.db = mysql.connector.connect(user='root', password='root', database='mysql')
        except mysql.connector.errors.Error as err:
           resp = jsonify({'status': 500, 'error': "Error:{}".format(err)})
           resp.status_code = 500
           return resp

    @app.route('/')
    def input_info(self):
        try:     
            cursor = g.db.cursor()
            cursor.execute ('CREATE TABLE IF NOT EXISTS testmysql (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(40) NOT NULL, \
                     email VARCHAR(40) NOT NULL UNIQUE)')
            cursor.close()

test.py

from app import *
class Test(unittest.TestCase):         
    def test_connection1(self):  
        with patch('__main__.mysql.connector.connect') as mock_mysql_connector_connect:
            object = TestMySQL()
            object.before_request()  # Runtime error on calling this

我正在将app导入到test.py中进行单元测试。在调用test.py中的'before_request'函数时,它会抛出一个运行时错误:working outside of application context,同样的情况也会发生在调用'input_info()'时。
6个回答

129

Flask有一个应用上下文,似乎你需要做这样的事情:

def test_connection(self):
    with app.app_context():
        #test code

您可能还可以将app.app_context()调用放入测试设置方法中。


1
@brenns10,请问您能详细讲解一下这个问题吗?为什么会出现这种情况? - aks
1
@aks 链接页面可能会比我更好地解释。基本上,Flask的设计方式使得(1)代码应该能够访问包含当前Flask应用程序对象的全局变量,但是(2)在单个进程中可能存在多个应用程序。 Flask通过在处理请求之前设置一些变量来管理这个问题。但是当你进行测试时,你必须自己设置"应用程序上下文",以便你的代码可以找到正确的全局变量。 - brenns10
22
我因使用 jsonify 而得到此错误。一个简单的 JSON 转换需要应用程序“在上下文中”运行,这真的很糟糕。 - milosmns
1
@milosmns:jsonify 不仅仅是简单的 JSON 转换。它实际上构建了一个适当的 HTTP 响应。如果您想要简单的 JSON 转换,可以直接使用 json.dumps()。否则,我打赌即使 flask.json.dumps 也需要上下文。 - Eddie Parker
1
@Eddie Parker:我想这正是我不喜欢的地方。我期望基本(或更好:默认,众所周知)功能可以在没有任何其他要求(如应用上下文)的情况下运行...然后通过添加额外的功能(如HTTP)通过某些选择性API进行组合。也许这也是一个沟通/文档问题。我不知道什么被更多地使用 - 纯JSON转换还是JSON+HTTP,所以我可能错误地认为大多数人只想要纯粹的转换。 - milosmns
显示剩余2条评论

19
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todo.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

app.app_context().push()

Run in terminal
    >python
    >>>from app import app
    >>>from app import db
    >>>db.create_all()

Now it should work

1
好的,它已经起作用了。谢谢你。 - thegreytangent

6

当我使用pytest时,遇到了类似的问题,我按照 @brenns10 的答案进行了操作。

我遵循把它放在测试设置中的建议,这种方法可行:

import pytest
from src.app import app


@pytest.fixture
def app_context():
    with app.app_context():
        yield


def some_test(app_context):
    # <test code that needs the app context>

1

我正在使用 Python3.8,并且必须对已发布的答案进行小变化。 我在 pytests 中包含了以下内容,而不必更改测试文件中的其他任何内容。

from flask import Flask

@pytest.fixture(autouse=True)
def app_context():
    app = Flask(__name__)
    with app.app_context():
        yield

这也可以与上下文管理器一起使用。 这里需要注意的主要区别是在测试文件中创建Flask应用程序,而不是从主应用程序文件导入它。


0
我遇到这个错误是因为我在应用程序内部创建了一个线程。
from threading import Thread

Thread(target=handle_offchain).start()

当Flask应用程序接收到请求时,Flask会创建应用程序上下文和请求上下文请求上下文来处理该特定请求。

请求上下文在请求期间跟踪请求级别的数据。而不是将请求对象传递给每个在请求期间运行的函数,而是访问请求和会话代理。

当Flask应用程序处理请求时,它根据从WSGI服务器接收到的环境创建一个请求对象。因为一个工作进程(线程、进程或协程,取决于服务器)一次只处理一个请求,所以在该请求期间,请求数据可以被视为对该工作进程全局可见。Flask将此称为上下文本地。

这是应用程序上下文

应用程序上下文在请求、CLI命令或其他活动期间跟踪应用程序级别的数据。而不是将应用程序传递给每个函数,而是访问current_app和g代理。

Flask的上下文本地变量(current_app、request等)与处理当前请求的线程绑定在一起,默认情况下它们不会在其他线程中共享或可用。如果在路由处理程序或视图函数中手动创建一个单独的线程,它不会自动访问Flask应用程序或请求上下文。
     def handle_offchain(app_context):
        # this will give access to context
        app_context.push()
        ....
    
    # args will be passed to  `handle_offchain` method
    Thread(target=handle_offchain,args=[current_app.app_context()]).start()

-1

对我来说,问题是在终端上运行时出现的。为了解决这个问题,我做了两件事:

  1. 在定义app.config[...]和创建db对象之后,添加了下面这行代码;

    app.app_context().push()

  2. 在终端上,我还从应用程序文件中导入app

    from name_of_your_app import app


这似乎是与此现有答案中提供的解决方案相同? - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接