Implementing dcc.Store on multi page app

This is more of an overall design/architecture question. I have an app with 4 pages on it and currently each page connects back to the database each time a control is changed which doesn’t entirely efficient.

Enter the dcc.Store component which seems amazing so is the best practice to create 1 store per app or create 1 store per page and which type of store is recommended? I was thinking of doing store based on session since multiple users can be on this dash app at a time.

Any thoughts welcome. Thanks!!

Anyone have any suggestions on this?

Thanks!

Hi @tphil10

I’m not sure there if there is one “best practice” – it really depends on how you use the data in your app.

For example you could use one dcc.Store() for your entire app and have the data for each page in a dictionary. However, if you use that as an Input in several callback, every time time any of the data changes, it will trigger the callbacks. Maybe you want that to happen … or maybe you only want the callback to trigger when the data for that one page changed.

The storage_type is how it’s stored in each user’s browser:

storage_type ( a value equal to: ‘local’, ‘session’, ‘memory’ ; default 'memory' ): The type of the web storage. memory: only kept in memory, reset on page refresh. local: window.localStorage, data is kept after the browser quit. session: window.sessionStorage, data is cleared once the browser quit.

Yea i’ll have to think around this. Each of my pages references the same kind of data but different timeframes of it. For example my first page is only looking at data that was gathered today whereas the second page is analyzing data that’s been gathered over the past 2 years.

I’m thinking i can create a single store for all the pages and load up the data upon the page changing. Then any filters the user uses on that page will retrieve from the store instead of having to go back to the database.

Hi @tphil10
I’m new to dash and struggling with actually implementing this. I believe there’s no documentation to this as well.
I have a dcc.Store() in the main app. Now how do I access this from different pages. I tried directly accessing from the callback, but it didn’t work. I tried importing the main app’s layout into the other page, that too didn’t work. I’m confused.

Could you please provide an example that shows how to use dcc.Store() in multipage apps.

Hi @rics
if you have dcc.Store storing data on the main app page. You should be able to use the Callback Input on any other page to retrieve the data. For example:

@app.callback(
    Output(.....)
    Input('stored_data_from_main_page', 'data')
)
def update_something(data):
    DoSomethingWithData
    return something

Did that work for you?

Hey @adamschroeder

Thanks for the response!
But unfortunately that didn’t work.
It gives the error: A nonexistent object was used in an Input of a Dash callback. The id of this object is intermediate_store_data and the property is data. The string ids in the current layout are: [url, page_content, DatePickerRange5, data_table_delay5]

where my main app page looks like:

app1 = Dash(__name__, suppress_callback_exceptions=True, external_stylesheets=[dbc.themes.BOOTSTRAP])

app1.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='page_content')
])

index_page = dbc.Container([
                                               dbc.Row([........]),
                                               dbc.Row([........]),
                                               dcc.Store(id='intermediate_store_data', data=[], storage_type='memory')
                        ])

The other page(not main app page) where I need the output from the dcc.Store has the layout which looks like this:

delay5_layout = dbc.Container([
    dbc.Row([
        dbc.Col([
            dcc.DatePickerRange(id='DatePickerRange5',
                                start_date=date.today(),
                                end_date=date.today()),
        ]),
    ]),

    html.Br(),

    dbc.Row(id='data_table_delay5',
            children=[]
            ),

], style={'padding': 10})

Your problem is the storage_type. The ‘memory’ store reverts to the default on every page refresh so you have to set the storage_type in other mode/type like ‘session’, because navigate between pages has the same effect that page refreshing.

Check the storage types in:

Store documentation

2 Likes

Hey @jeffresh
I tried all three storage types, but it shows the same error.

It’s seems like a page structure problem. Do you have a callback in the same scope than the app.layout that handles the pages content? something like:

@callback(Output('page-content', 'children'),
          Input('url', 'pathname'))
def display_page(pathname: str) -> dbc.Container:

    if pathname == '/':
        return page1.layout
    if pathname == '/page2':
        return page2.layout
    return error_page.layout

Yes, my main page looks like this:

homepage.py

app1 = Dash(**name**, suppress_callback_exceptions=True, external_stylesheets=[dbc.themes.BOOTSTRAP])

app1.layout = html.Div([
dcc.Location(id=‘url’, refresh=False),
html.Div(id=‘page_content’)
])

index_page = dbc.Container([
dbc.Row([…]),
dbc.Row([…]),
dcc.Store(id=‘intermediate_store_data’, data=[], storage_type=‘memory’)
])

@callback(
    Output('intermediate_store_data', 'data'),
    Input('datePickerRange', 'start_date'),
    Input('datePickerRange', 'end_date'),
    Input('dropdown_id', 'value'),
    Input('order_status', 'value'),
    Input('service', 'value')
)
def intermediate_data(a,b,c,d,e):
-------
return

@callback(
    Output'total_order_count', 'children'),
    Input('intermediate_store_data', 'data'),
    
)
def homepage(a):
--------
return

@app1.callback(
    Output('page_content', 'children'),
    Input('url', 'pathname')
)
def display_page(pathname):
    if pathname == '/delay5':
        return delay5_layout
    elif pathname == '/delay10':
        return delay10_layout
    else:
        return index_page

And my other page looks like this:

delay5_report.py

delay5_layout = dbc.Container([
dbc.Row([
dbc.Col([
dcc.DatePickerRange(id=‘DatePickerRange5’,
start_date=date.today(),
end_date=date.today()),
]),
]),

html.Br(),

dbc.Row(id='data_table_delay5',
        children=[]
        ),

], style={‘padding’: 10})

In delay5_report.py, I can’t access the dcc.Store ‘intermediate_store_data’ data. Works fine for homepage.py

@rics - I’m not sure I understand how you are structuring your app, but you could try using pages to make a multi-page app - it’s new in dash 2.5.1. See the updated docs here: Multi-Page Apps and URL Support | Dash for Python Documentation | Plotly

For an example, see the folder multi_page_store/ for an app that shares data between callbacks on different pages using a dcc.Store component.

It’s seems correct so the only thing for me that could be the reason is the imports. But first of all, dont mix @app.callback and @callback dont know if woud be any problem but if you use @callback, use it for your entire app just to keep consistency.

Then I recommend you to import the callbacks of delay_5 within delay5_report.py. Then import delay5_report.py page in homepage.py

The error is because delay5_report5 callback “cannot see” the store. To check that your previous code is correct, try first to copy the code (layout and callbacks) of report_5 in homepage.py. If it works, the error is due to the way thay your are doing your imports.

Hey @jeffresh
I found out the issue. I was using the dcc.Store in my page layout but instead that needs to be defined in the main app layout where url and page-content is defined.

Now I can access the store data from anywhere just as @adamschroeder said.

Thanks both of you! :slight_smile:

2 Likes

Hi Adam, can we store data with dcc.Store in a normal page (page1) and then use the data in another page (page2)? I’m trying to do it , but it only works in the main app page.

Hello @jalcaide .

If you create your dcc.Store in app.layout definition (variable or function) it should be available to all pages.

Be careful, if you have multipage app and you create your dcc.Store in one of the pages then it does not stay in layout definition when you access another page.

3 Likes

Thank you Martin
do you know if there is a way to treat data in one page and then use the same data in another page?
The only way I found, is saving the data in a local csv file, and then open the csv file inside a callback in another page.

Before I answer, it depends how big is your data. When it is quite small (i.e. < 10MB) it fits the dcc.Store. If it is larger then some background caching method will be required. It also depends what “treat” means, users input can be transfered from one page to another so sometimes you don’t have to save all the data, just the transformation.

If you will be more specific we can try to find the best solution :slight_smile:

Late but hope help someone:


# Here import the Div wich store you dcc.stores like

from services.biology import biology_stores


# App Config
app = dash.Dash(
    __name__,
    use_pages=True,
    external_stylesheets=external_stylesheets,
    meta_tags=[
        {"name": "viewport", "content": "width=device-width, initial-scale=1.0"}
    ],
)


server = app.server

# append that Div to page_container (if you look at the core code of Dash, 'page_container' is a Div, so, you can append to his children all of your stores

#! stores
dash.page_container.children.append(biology_stores.render())
#!

app.layout = dash.page_container
biology_store.py

def render():
    return html.Div(
        [
            dcc.Store(
                id="biology-stored_data",
                storage_type="local",
            ),
            dcc.Store(
                id="biology-dataframe_filters_store",
                storage_type="local",
            ),
            dcc.Store(
                id="biology-zone_headers_store",
                storage_type="local",
            ),
            dcc.Store(
                id="biology-taxonomy_headers_store",
                storage_type="local",
            ),
        ]
    )

1 Like

I too am getting the annoying “A nonexistent object was used in an Input of a Dash callback” error when using dcc.Store in a dash app using Pages. I have spent the last two days reading every post on the topic and trying every solution I could find with no success. supress_callback_exceptions = True doesn’t fix anything, changing the Input to a State doesn’t fix anything, moving the logic setting dcc.Store into its own callback doesn’t fix anything, I’ve tried moving dcc.Store in app.layout, I’ve tried layout functions vs variables, I’ve tried different storage settings, but nothing works.

I couldn’t even manage to replicate the issue in a MRE, probably because of how complicated my actual layout is

My first question: Does this even matter? The code works. It just throws the error every time I start the app on the wrong page (any page other than the page where the input actually exists). When I turn off debugging and throw the app up on the web (AWS Elastic Beanstalk), will my end users even notice the warning? Any long term issues with just ignoring this?

I am going to describe my setup anyway on the off chance that it triggers something in someone:

app.py - has fixed navigation in the form of two dropdowns (year and entity). Layout looks something like:

app.layout = html.Div( 
      [
            dcc.Store(id="store", data = {}, storage_type = "session"),
            html.Div(
                  [
                      dcc.Dropdown(
                       id="entity-dropdown",
                       multi=False,
                       clearable=False,
                      ),
                      dcc.Dropdown(
                       id="year-dropdown",
                       multi=False,
                       clearable=False,
                      ),
                  . . . rest of navigation  . . .
                  ]
            ),
            html.Div(
                  [
                    dbc.Nav(
                       [
                             dbc.NavLink(
                                 "Page1",
                                  href="/",
                                  className="tab",
                                  active="exact"
                             ),
                             ... more pages . . .
                              dbc.NavLink(
                                 "Page6",
                                  href="/page6",
                                  className="tab",
                                  active="exact"
                             ),

                       ]
                   ),
                   dash.page_container,
                 ]
            )
    ]
)

There are 8 pages at the top_nav level. Pages are hardcoded for formatting reasons. the @callback for year-dropdown has a bunch of logic for determining what set of years should be displayed.

Page6 (/page6) has two buttons, [‘Red’] and [‘Green’]. These buttons determine what charts are displayed on page 6, but they also must change the years that are displayed in ‘year-dropdown.’ So, i have the following callback in page6.py which: 1) determines whether or not to display the buttons at all depending on entity name &, if buttons should be displayed, sets the options and value and then captures the value to the dcc.Store variable:

@callback(      
    Output("radio-options", "options"),
    Output("radio-value", "value"),
    Output("store", "data"),
    Input("entity-dropdown", "value"),
    Input("radio-value", "value"),
    supress_callback_exceptions = True
)

def radio_selector(entity: str, radio_value: str):

... logic deterimining whether radio buttons should be displayed depending on entity name 
    and then setting options and value. . .

 store_value = radio_value

return radio_options, radio_value, store_value

The @callback for dropdown-year in app.py then reads the store and adjusts the years displayed in the dropdown accordingly.

This all works! Except for the error. Spent an hour this morning trying to modify @AnnMarieW 's Multipage Store example to fit my use case, but couldn’t replicate my issue.

Does anyone have any other suggestions? I’m not even married to the idea of using dcc.store (it is the only thing I am using it for). I just need to get a single string variable set by a callback in page6.py to a callback in app.py. If there is another way to do this, please let me know!