【问题标题】:Flask Application Context: app.app_context().push() works but can't get "with app.app_context()" block to workFlask 应用程序上下文:app.app_context().push() 有效,但无法让“with app.app_context()”块工作
【发布时间】:2019-08-30 23:34:21
【问题描述】:

我是 flask 的新手,只编写了一个相当简单的网络应用程序——没有数据库,只是一个航班搜索 API 的前端。一切正常,但为了提高我的技能,我正在尝试使用应用程序工厂和蓝图重构我的代码。让它与push() 一起工作,但不能让with 块工作。

fly_app/__init.py__

我的原始代码:

from flask import Flask
from config import Config
import logging
from logging.handlers import RotatingFileHandler
import os

app = Flask(__name__)
app.config.from_object(Config)

if not app.debug:
    ...

from fly_app import routes, errors  # noqa

我根据需要将应用导入到其他模块。

现在该文件已被重构为这个并且似乎可以工作:

from flask import Flask
import logging
from logging.handlers import RotatingFileHandler
import os


def create_app():
    # create and configure the app
    app = Flask(__name__)
    app.config.from_mapping(
        SECRET_KEY=os.environ.get('SECRET_KEY', 'nice-try')
    )
    app.app_context().push()

    if not app.debug and not app.testing:
        ...

    from . import routes
    app.register_blueprint(routes.bp)

    return app

我需要 app_context 以便可以在其他模块中使用 current_app.static_folder 和 current_app.logger.exception()。我读过使用with 块,例如,

with app.app_context():
    init_db()

优于

app.app_context().push()

来自 Flask 文档:“在 with 块中使用 app_context(),并且在该块中运行的所有内容都可以访问 current_app。”

我的问题:

  1. with 块有何不同或优于使用 app.app_context().push()?
  2. 在我的原始代码中使用push() 是否等于将app 设置为全局变量,从而一无所获?
  3. 我无法弄清楚如何在不引发“RuntimeError:在应用程序上下文之外工作”的情况下让with 块工作。我尝试了with 块,包括:
    • app.static_folder
    • current_app(导入后)或current_app.[attr]
    • 特定的 module.methods() 或使用 current_app 的属性

例如,我编写了两个自定义验证器 - code_check 和 currency_check - 用于从 forms.py 模块中的 flask_wtf.FlaskForm 派生的类。这些采用 formfield 位置参数,我无法将它们放入 with 块中,因为它们未定义。

作为一个相对新手,尽管阅读文档和搜索互联网,但我显然不了解应用程序上下文的工作原理。

【问题讨论】:

    标签: flask


    【解决方案1】:

    我想我可以充分回答我上面的三部分问题。首先,第三部分——在with 块中放入什么。这个答案简单得令人尴尬:

    fly_app.__init__.py
    
    from flask import Flask
    ...
    
    
    def create_app():
        ...
    
        with app.app_context():
            from . import routes
            app.register_blueprint(routes.bp)
    
        return app
    

    我需要做的就是将蓝图注册放在with 块中。在那之后一切似乎都正常了。 (最终,我决定朝那个方向发展(解释如下)。

    关于第 1 部分和第 2 部分——with 块与 push() 有何不同,push() 是否授予对应用程序上下文的全局访问权限?我不确定这些答案,但在解释器中玩耍给了我一些见解。当我在 create_app() 函数中使用 app.app_context().push() 时:

    >>> import fly_app
    >>> from flask import current_app
    >>> current_app
    <LocalProxy unbound>
    >>> fly_app.create_app()
    <Flask 'fly_app'>
    >>> current_app
    <Flask 'fly_app'>
    >>> current_app.static_folder
    '/Users/Steve/.../fly_app/static'
    

    current_app 及其属性立即可用。但是,如果我使用with 块导入和注册我的routes.bp,我会得到不同的结果。 current_app 未公开绑定或可用:

    >>> import fly_app
    >>> from flask import current_app
    >>> current_app
    <LocalProxy unbound>
    >>> fly_app.create_app()
    <Flask 'fly_app'>
    >>> current_app
    <LocalProxy unbound>
    >>> fly_app.routes.current_app.static_folder
    Traceback (most recent call last):
      ...
    RuntimeError: Working outside of application context.
    

    code_dir = os.path.join(current_app.static_folder, 'JSON/optd_codes.json') 仍然有效:

    >>> fly_app.routes.code_dir
    '/Users/Steve/Documents/flask_requests_env/dragonfly/fly_app/static/JSON/optd_codes.json'
    

    所以我从这些观察中推断出app.app_context().push() 似乎确实使应用程序上下文全局可用,但使用with 块却没有。或者在我的非 CS 解释中,应用程序上下文是可用的,但更严格地说,即仅适用于我的应用程序的指定部分。

    最终,感谢 David W. Smith 的指导(请参阅他的回答以及我在 cmets 中与他的对话),我认为需要静态文件夹的路径并不是手动推送应用程序上下文的好理由。我为我的Blueprint 使用了static_folder 关键字参数,并将current_app.static_folder 替换为bp.static_folder。无需手动推送。

    【讨论】:

      【解决方案2】:

      在 Flask 新手时将来自多个来源的代码拼凑在一起是一种挫败感。我看到了你前进的方向,强烈建议你通读 Miguel Grinberg 的Flask Mega Tutorial。它提供了一条路径,可以让您更好地了解 Flask 的工作原理。

      (提示:app.app_context().push() 中不需要 create_app()

      【讨论】:

      • 谢谢。我首先在 Miguel 的教程中学习了 Flask,但从未完成它,因为我拥有让我的简单航班搜索应用程序正常工作所需的所有部分。现在,作为一种学习体验,我正在尝试使用应用程序工厂重构我的代码。我浏览了 Flask 的tutorial。我还回到了 Miguel 的教程,第 XV 部分,更好的应用程序结构。他创建了扩展实例,并在他的应用程序工厂中调用了 instance.init_app(app)。这似乎与我的情况无关。如果我不 push() 我得到上下文错误。
      • 您将需要app.app_context().push()(或with app.app_context(): ...,只要您需要与应用程序一起配置的东西(例如db,这取决于db.init_app(app) 来配置它),或者你需要做一些 Flask 需要请求上下文的事情。在 create_app() 中做这件事会使 Flask 机器处于不寻常的状态,这可能会在其他地方给你带来神秘的问题。
      • 再次感谢。我认为您是说如果可以避免,最好手动推送应用程序上下文。来自 Flask 文档:“Flask 在处理请求时自动推送应用程序上下文。”但我觉得我需要为一个简单的任务推送应用程序上下文,在请求之外定义 JSON 文件的路径,例如:code_dir = os.path.join(current_app.static_folder, 'JSON/optd_codes.json') 所以我找到了解决方法,通过设置:bp = Blueprint('routes', __name__, static_folder='./static')code_dir = os.path.join(bp.static_folder, 'JSON/optd_codes.json') Better?
      • 看起来更好。您仍然需要应用程序上下文来处理请求上下文之外的事情(例如,用于单元测试和 cli 内容),但是让(推送)该上下文尽可能接近您需要的事情将为您省去调试的麻烦。跨度>
      • 您好,您提到“在 create_app() 中执行此操作会使 Flask 机器处于异常状态,这可能会导致您在其他地方出现神秘问题”。假设我在初始化文件中有带有工厂 create_app 的包。在工厂中我想导入views.py(views.py 使用@current_app.routes 来配置路由),然后我必须推送app.context 来导入views.py 文件。否则,它会给出我在应用程序上下文之外工作的错误。有没有其他选择?我知道蓝图是一种替代方案,但如果我的应用只有 4 条路线,那么我不想制作蓝图。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-11
      • 2016-07-12
      • 2020-10-13
      • 1970-01-01
      • 2018-11-08
      • 1970-01-01
      相关资源
      最近更新 更多