我有一个 Dash 应用程序,用户在一个页面
data.py
中添加一些数据,然后可以在另一页面 grid.py
上查看和删除选定的行。用户稍后应该能够返回 data.py
并添加更多数据。
问题:在访问
grid.py
之间数据不会保留。我怎样才能做到这一点?我尝试设置 persistence
属性,但这并没有让我有任何进展。 existing_data
中的grid.py
始终是None
。当我使用单页应用程序时,类似的代码可以工作。
这是我的最小可重现示例:
app.py
from dash import html, dcc
import dash
app = dash.Dash(__name__, use_pages=True)
app.layout = html.Div(
[
dcc.Store(id="store", data={}),
html.H1("Multi Page App Demo: Sharing data between pages"),
html.Div(
[
html.Div(
dcc.Link(f"{page['name']}", href=page["path"]),
)
for page in dash.page_registry.values()
]
),
html.Hr(),
dash.page_container,
]
)
if __name__ == "__main__":
app.run_server(debug=True)
数据.py
from dash import html, Input, Output, callback, register_page
from dash.exceptions import PreventUpdate
import random
register_page(__name__, path="/")
layout = html.Div(
[
html.H3("Data input"),
html.Button("Add row", id="button_id"),
html.Br(),
html.Div(id="my-output"),
]
)
@callback(
[Output("store", "data"), Output("my-output", "children")],
Input("button_id", "n_clicks"),
prevent_initial_call=True
)
def add_data(n_clicks):
if n_clicks:
new_data = [{"col1": "New row", "col2": random.randint(0, 1000)}]
return new_data, html.Pre(str(new_data))
else:
raise PreventUpdate
grid.py
from dash import html, dash_table, Input, Output, callback, register_page, State
register_page(__name__)
layout = html.Div([
html.H3("Data tables"),
dash_table.DataTable(
id="table",
row_deletable=True,
column_selectable="single",
page_size=5,
persistence=True,
persisted_props=[
"data",
"columns.name",
"filter_query",
"hidden_columns",
"page_current",
"selected_columns",
"selected_rows",
"sort_by",
],
),
])
@callback(
Output("table", "data"),
Input("store", "data"),
State("table", "data"),
)
def update(new_data, existing_data):
if existing_data is not None:
return existing_data + new_data
else:
return new_data
我想出了两种方法:
Store
组件。为了完整起见,我将分享两者。拥有(2)的一个原因是我不完全理解为什么(1)有效,据我所知它引入了循环依赖。后者应该抛出异常,或者更糟糕的是,导致无限的回调循环,但事实并非如此。这对于
Dash
来说很聪明,但我不能确定它总是有效 - 它没有记录在案。
共享
Store
组件
app.py:
from dash import html, dcc
import dash
app = dash.Dash(__name__, use_pages=True)
app.layout = html.Div(
[
dcc.Store(id="store", data=[], storage_type="session"),
html.H1("Multi Page App Demo: Sharing data between pages"),
html.Div(
[
html.Div(
dcc.Link(f"{page['name']}", href=page["path"]),
)
for page in dash.page_registry.values()
],
id='navbar'),
html.Hr(),
dash.page_container
]
)
if __name__ == "__main__":
app.run_server(debug=True)
页面/data.py
from dash import html, Input, Output, callback, register_page, State
from dash.exceptions import PreventUpdate
import random
register_page(__name__, path="/")
layout = html.Div(
[
html.H3("Data input"),
html.Button("Add row", id="button_id"),
html.Br(),
html.Div(id="my-output"),
]
)
@callback(
[Output("store", "data"), Output("my-output", "children")],
Input("button_id", "n_clicks"),
State("store", "data")
)
def add_data(n_clicks, data):
if n_clicks:
new_data = [{"col1": "New row", "col2": random.randint(0, 1000)}]
return data + new_data, html.Pre(str(new_data))
else:
raise PreventUpdate
pages/grid.py:
from dash import html, dash_table, Input, Output, callback, register_page
register_page(__name__)
layout = html.Div([
html.H3("Data tables"),
dash_table.DataTable(
id="table",
data=[{"name": "Test", "label": "Test"}],
row_deletable=True,
column_selectable="single",
page_size=5,
persistence=True,
persisted_props=["columns.name", "data"],
),
])
@callback(
Output("table", "data"),
Input("store", "data"),
)
def add_rows(new_data):
return new_data
@callback(
Output("store", "data", allow_duplicate=True),
Input("table", "data"),
prevent_initial_call=True
)
def update_back(new_data):
return new_data
使用
dash_extensions.pages
中的持久组件功能
app.py:
from dash import html, dcc
from dash_extensions.pages import setup_page_components
from pages.components import NAVBAR_ID
import dash
app = dash.Dash(__name__, use_pages=True)
app.layout = html.Div(
[
dcc.Store(id="store", data=[], storage_type="session"),
html.H1("Multi Page App Demo: Sharing data between pages"),
html.Div(
[
html.Div(
dcc.Link(f"{page['name']}", href=page["path"]),
)
for page in dash.page_registry.values()
],
id=NAVBAR_ID),
html.Hr(),
dash.page_container,
setup_page_components()
]
)
if __name__ == "__main__":
app.run_server(debug=True)
页面/组件.py
from dash import dash_table
NAVBAR_ID = "navbar"
data_table = dash_table.DataTable(
id="table",
row_deletable=True,
column_selectable="single",
page_size=5,
persistence=True,
persisted_props=[
"data",
"columns.name",
]
)
页面/data.py
from dash import html, Input, Output, callback, register_page
from dash.exceptions import PreventUpdate
import random
register_page(__name__, path="/")
layout = html.Div(
[
html.H3("Data input"),
html.Button("Add row", id="button_id"),
html.Br(),
html.Div(id="my-output"),
]
)
@callback(
[Output("store", "data"), Output("my-output", "children")],
Input("button_id", "n_clicks")
)
def add_data(n_clicks):
if n_clicks:
new_data = [{"col1": "New row", "col2": random.randint(0, 1000)}]
return new_data, html.Pre(str(new_data))
else:
raise PreventUpdate
页面/grid.py
from dash import html, Input, Output, callback, register_page, State
from pages.components import data_table
register_page(__name__, page_components=[data_table])
layout = html.Div([
html.H3("Data tables"),
])
@callback(
Output("table", "data"),
Input("store", "data"),
State("table", "data")
)
def add_rows(new_data, old_data):
if old_data:
return old_data + new_data
return new_data