使用应用程序工厂模式时,如何从嵌入式Dash应用程序中访问Flask应用程序的上下文?

时间:2019-06-25 12:33:17

标签: python-3.x flask flask-sqlalchemy plotly-dash

我正在使用Flask和Dash构建一个Web应用程序。 Flask主应用处理用户登录和身份验证,并保护Dash应用的路由。 Dash应用程序是从Flask应用程序中提供的。

主要的Flask应用是使用Flask Mega Tutorial示例(https://github.com/miguelgrinberg/microblog)构建的,具有应用工厂模式。我删除了除登录/身份验证之外的所有内容,并且该应用程序在该阶段可以正常运行。

然后我按照此处的示例https://github.com/okomarov/dash_on_flask在Flask应用程序中添加了一个简单的Dash应用程序。可以在/ dashboard上访问Dash应用程序,并且该仪表板受到了适当的保护,可防止未经授权的访问,并按预期重定向到Flask主登录页面。

我现在面临的挑战是从Dash应用程序中访问Flask应用程序的上下文以及诸如数据库会话之类的其他内容。这样Dash应用程序可以访问和显示主数据库中的信息。

我目前一直在努力跟踪我在网上找到的示例,以及如何使它们适应我的应用程序的特定模式。

我尝试遵循以下链接的示例:https://github.com/plotly/dash/issues/214#issuecomment-391223557

但是,这些示例似乎全部基于平面应用程序结构,其中Flask和Dash应用程序是在同一文件中创建的。

我所有的代码都位于此处:https://github.com/danielcopelin/dacy-budget

此刻,我的主要Flask应用看起来像:

dacybudget.py

from app import create_app, db
from app.models import User

app = create_app()


@app.shell_context_processor
def make_shell_context():
    return {'db': db, 'User': User}

create_app()如下:

def create_app(config_class=Config):
    app = Flask(__name__)
    app.config.from_object(config_class)

    register_dashapps(app)

    db.init_app(app)
    migrate.init_app(app, db)
    login.init_app(app)
    mail.init_app(app)
    bootstrap.init_app(app)

...和register_dashapps()如下:

def register_dashapps(app):
    from app.dashapp1.layout import layout
    from app.dashapp1.callbacks import register_callbacks

    # Meta tags for viewport responsiveness
    meta_viewport = {
        "name": "viewport",
        "content": "width=device-width, initial-scale=1, shrink-to-fit=no",
    }
    external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
    dashapp1 = dash.Dash(
        __name__,
        server=app,
        url_base_pathname="/dashboard/",
        assets_folder=get_root_path(__name__) + "/dashboard/assets/",
        meta_tags=[meta_viewport],
        external_stylesheets=external_stylesheets,
    )

    dashapp1.title = "Dashapp 1"
    dashapp1.layout = layout
    dashapp1.url_base_pathname = "/dashboard/"  # I dont know why I had to do this
    register_callbacks(dashapp1)
    _protect_dashviews(dashapp1)

从app.dashapp1.layout和app.dashapp1.callbacks内部,我希望能够访问Flask应用程序的主会话和数据库等。

如果我尝试从app.dashapp1.layout中进行各种导入,例如“ from .. import db”,然后尝试对该数据库对象执行某些操作,则会出现类似以下错误:

RuntimeError:未找到应用程序。在视图函数内部工作或推送应用程序上下文。参见http://flask-sqlalchemy.pocoo.org/contexts/

有人可以提供有关如何解决此问题的建议吗?

2 个答案:

答案 0 :(得分:0)

我设法解决了我的问题。我在努力了解该应用程序Dash端中可以访问Flask主应用程序上下文的位置,以便为Dash提供对数据库的访问。事实证明,可以在register_callbacks()函数和其中定义的实际回调函数中完成此操作,

def register_callbacks(app):
    from app import db
    from app.models import Transaction
...
    @app.callback(
            Output("main", "children"),
            [Input("transaction_table", "data_previous")],
            [
                State("transaction_table", "data"),
                State("transaction_table", "page_current"),
            ],
        )
        def update_database_and_generate_table(old_table_data, table_data, page_current):
            with app.server.app_context():
                if old_table_data is not None:
                    update_changed_data(old_table_data, table_data)
                transactions = db.session.query(Transaction)
                df = pd.read_sql(transactions.statement, transactions.session.bind)

要使其正常工作,必须对原始问题的代码进行另一处更改。在create_app()函数中,您需要在数据库初始化后注册Dash应用程序:

def create_app(config_class=Config):
    app = Flask(__name__)
    app.config.from_object(config_class)

    db.init_app(app)
    migrate.init_app(app, db)
    login.init_app(app)
    mail.init_app(app)
    bootstrap.init_app(app)

    app = dashapp.add_dash(app)
...

完整的工作示例位于此处:https://github.com/danielcopelin/dacy-budget

答案 1 :(得分:0)

您的答案在这里Project Layout。 我遇到了类似的问题,无法在flask应用程序中执行“常规” python导入。

按照建议的名称命名应用程序 flaskr ,然后将 app.py 模板移到此处...就像在本教程中一样。它对我有用。