运行 多个具有服务布局功能的 Dash 应用程序

Run multiple Dash apps with serve layout functions

我目前正在尝试 运行 多个具有服务布局功能的 Plotly Dash 应用程序(请参阅下面的代码以及 serve_layout1() 和 serve_layout2() 功能)。我需要它们,因为我必须在布局中包含几个全局变量,这样当有人在浏览器中单击刷新按钮时,我的应用程序就会更新。多个 Dash 应用程序在没有服务布局功能的情况下也能正常工作。但是,当我尝试 运行 多个具有服务布局功能的应用程序时,事情开始出错。

这是我的文件结构:

- app.py
- index.py
- apps
   |-- __init__.py
   |-- app1.py
   |-- app2.py

其中app.py:

import dash
app = dash.Dash(__name__, suppress_callback_exceptions=True)
server = app.server

index.py:

import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

from app import app
from apps import app1, app2

from apps.app1 import serve_layout1
from apps.app2 import serve_layout2

app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='page-content')
])


@app.callback(Output('page-content', 'children'),
              Input('url', 'pathname'))
def display_page(pathname):
    if pathname == '/apps/app1':
        app1.layout = serve_layout1
        return app1.layout
    elif pathname == '/apps/app2':
        app2.layout = serve_layout2
        return app2.layout
    else:
        return '404'

if __name__ == '__main__':
    app.run_server(debug=True)

app1.py:

import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from app import app

def serve_layout1():

    global df

    # uploading cvs as dataframe, doing some computation here and etc (skipped)

    app_layout1 = html.Div([
        html.H3('App 1'),
        dcc.Dropdown(
            id='app-1-dropdown',
            options=[
                {'label': 'App 1 - {}'.format(i), 'value': i} for i in [
                    'NYC', 'MTL', 'LA'
                ]
            ]
        ),
        html.Div(id='app-1-display-value'),
        dcc.Link('Go to App 2', href='/apps/app2')
    ])

    return app_layout1

@app.callback(
    Output('app-1-display-value', 'children'),
    Input('app-1-dropdown', 'value'))
def display_value(value):
    return 'You have selected "{}"'.format(value)

app2.py:

import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from app import app

def serve_layout2():

    global df

    # uploading cvs as dataframe, doing some computation here and etc (skipped)

    app_layout2 = html.Div([
        html.H3('App 2'),
        dcc.Dropdown(
            id='app-2-dropdown',
            options=[
                {'label': 'App 2 - {}'.format(i), 'value': i} for i in [
                    'NYC', 'MTL', 'LA'
                ]
            ]
        ),
        html.Div(id='app-2-display-value'),
        dcc.Link('Go to App 2', href='/apps/app1')
    ])


    return app_layout2


@app.callback(
    Output('app-1-display-value', 'children'),
    Input('app-1-dropdown', 'value'))
def display_value(value):
    return 'You have selected "{}"'.format(value)

当我 运行 应用程序时,出现以下错误:

Traceback (most recent call last):
  File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\dash\dash.py", line 1039, in add_context
    response, cls=plotly.utils.PlotlyJSONEncoder
  File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\json\__init__.py", line 238, in dumps
    **kw).encode(obj)
  File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\_plotly_utils\utils.py", line 45, in encode
    encoded_o = super(PlotlyJSONEncoder, self).encode(o)
  File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\json\encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\json\encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\_plotly_utils\utils.py", line 115, in default
    return _json.JSONEncoder.default(self, obj)
  File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\json\encoder.py", line 180, in default
    o.__class__.__name__)
TypeError: Object of type 'function' is not JSON serializable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\flask\app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\dash\dash.py", line 1072, in dispatch
    response.set_data(func(*args, outputs_list=outputs_list))
  File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\dash\dash.py", line 1042, in add_context
    _validate.fail_callback_output(output_value, output)
  File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\dash\_validate.py", line 256, in fail_callback_output
    _validate_value(val, index=i)
  File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\dash\_validate.py", line 251, in _validate_value
    toplevel=True,
  File "C:\Users\iakubal\Anaconda3\envs\general_3_6\lib\site-packages\dash\_validate.py", line 205, in _raise_invalid
    bad_val=bad_val,
dash.exceptions.InvalidCallbackReturnValue: The callback for `<Output `page-content.children`>`
returned a value having type `function`
which is not JSON serializable.


The value in question is either the only value returned,
or is in the top level of the returned list,

and has string representation
`<function serve_layout1 at 0x0000016B49DABB70>`

In general, Dash properties can only be
dash components, strings, dictionaries, numbers, None,
or lists of those.

问题是您没有在此处调用 serve_layout 函数:

@app.callback(Output("page-content", "children"), Input("url", "pathname"))
def display_page(pathname):
    if pathname == "/apps/app1":
        app1.layout = serve_layout1
        return app1.layout
    elif pathname == "/apps/app2":
        app2.layout = serve_layout2
        return app2.layout
    else:
        return "404"

你应该这样做:

@app.callback(Output("page-content", "children"), Input("url", "pathname"))
def display_page(pathname):
    if pathname == "/apps/app1":
        app1.layout = serve_layout1()
        return app1.layout
    elif pathname == "/apps/app2":
        app2.layout = serve_layout2()
        return app2.layout
    else:
        return "404"

因此,您返回的是对函数的引用,而不是函数的破折号组件 returns。

这无效,因为您分享的错误消息是这样说的:

In general, Dash properties can only be dash components, strings, dictionaries, numbers, None, or lists of those.

同样在 app2.py 中,您在回调中引用了错误的 ID。您正在为 app1.py 引用 ids。您不能有重复的回调输出。所以 app2.py 中的回调应该是这样的:

@app.callback(
    Output("app-2-display-value", "children"), Input("app-2-dropdown", "value")
)
def display_value(value):
    return 'You have selected "{}"'.format(value)