Flask应用程序上下文:app.app_context()。push()可以工作,但无法使“ with app.app_context()”块正常工作

时间:2019-08-30 23:34:21

标签: flask

我对Flask是陌生的,只编写了一个相当简单的Web应用程序-没有数据库,只是一个航班搜索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()区块有何不同或更好? push()
  2. 使用app是否等于使with成为我的原始代码中的全局变量,从而什么也没做?
  3. 我不知道如何在不引发“ RuntimeError:在应用程序上下文之外工作”的情况下使with块正常工作。我尝试了app.static_folder块,包括:
    • current_app
    • current_app.[attr](导入后)或flask_wtf.FlaskForm
    • 特定于module.methods()或使用current_app的属性

例如,我编写了两个自定义验证器-code_check和currency_check-以与forms.py模块中从form派生的类一起使用。这些带有fieldBundler could not find compatible versions for gem "actionpack": In Gemfile: jquery-datatables-rails x64-mingw32 was resolved to 3.4.0, which depends on actionpack (>= 3.1) x64-mingw32 rails (= 5.2.3) x64-mingw32 was resolved to 5.2.3, which depends on actionpack (= 5.2.3) x64-mingw32 rspec-rails x64-mingw32 was resolved to 3.8.2, which depends on actionpack (>= 3.0) x64-mingw32 simple_form x64-mingw32 was resolved to 4.1.0, which depends on actionpack (>= 5.0) x64-mingw32 rails (= 5.2.3) x64-mingw32 was resolved to 5.2.3, which depends on sprockets-rails (>= 2.0.0) x64-mingw32 was resolved to 3.2.1, which depends on actionpack (>= 4.0) x64-mingw32 位置参数,由于它们未定义,我无法将它们放在with块中。

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

2 个答案:

答案 0 :(得分:0)

当Flask推出新产品时,将来自多个源的代码拼凑在一起是令人沮丧的秘诀。我知道了您的前进方向,并强烈建议您通读Miguel Grinberg的Flask Mega Tutorial。它提供了一条路径,可以使您更好地了解Flask的工作方式。

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

答案 1 :(得分:0)

我认为我可以完全回答我的上述三部分问题。首先,第三部分-在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()是否赋予对应用程序上下文的全局访问权限?我不确定这些答案,但是在口译员中玩耍给了我一些见解。当我在app.app_context().push()函数中使用create_app()时:

>>> 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)的指导(请参阅他的回答以及我和他的谈话中的注释),我决定需要一个指向静态文件夹的路径并不是手动推送应用程序上下文的一个好理由。我为static_folder使用了Blueprint关键字参数,并将current_app.static_folder替换为bp.static_folder。无需手动推送。