Multi-page Dash App call back not responding

Update: It seems like they work if i keep them in same file. But i dont want to do that.
I am using dash with flask. Both of them has multi-page. Everything in flask is working fine and dash is working too how ever callback in dash for page other than the base page is not working. Here is my code:
#main.py

    server = Flask(__name__)

    Dapp = dash.Dash(__name__, server=server, url_base_pathname='/test/')
    Dapp.scripts.config.serve_locally = True
    Dapp.config['suppress_callback_exceptions'] = True

    Dapp.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(dt.DataTable(rows=[{}]), style={'display': 'none'}),
     html.Div(id='page-content'),
                        ])

    Dapp.css.append_css({
    'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'
    })

    @server.route('/')
    def hello_world():
        return render_template('homepage.html')

    @server.route('/aboutUs')
    def pageAboutUs():
        return render_template('aboutUsPage.html')

    main_page = html.Div([html.H1('something')])

    @Dapp.callback(Output('page-content', 'children'),
                  [Input('url', 'pathname')])
    def display_page(pathname):
        print(pathname)
        if pathname == '/test/t2':
             return page2.layout
        else:
            return main_page
    if __name__ == '__main__':
        Dapp.run_server()

Here is page2.py:

      layout = html.Div([
                    dcc.Upload(
                    id='upload-data55',
                    children=html.Div([
                        'Drag and Drop or ',
                        html.A('Select File')
                    ]),
                    style={
                        'width': '100%',
                        'height': '60px',
                        'lineHeight': '60px',
                        'borderWidth': '1px',
                        'borderStyle': 'dashed',
                        'borderRadius': '5px',
                        'textAlign': 'center',
                        'margin': '10px'
                    },
                    multiple=False
                ),
                html.Div(id='output-data-upload55')
            ])

            @Dapp.callback(Output('output-data-upload55', 'children'),
                          [Input('upload-data55', 'contents'),
                           Input('upload-data55', 'filename'),
                           Input('upload-data55', 'last_modified')])
            def update_output(list_of_contents, list_of_names, list_of_dates):
                print('asd')
                return html.h1(list_of_contents+' '+list_of_names+' '+list_of_dates)

The update _output function is not firing how ever display_page is. It seems like page other than the base dash page is not calling back properly. What can i do?

1 Like

Hello @saadmahmud :slight_smile:

I’m not a pro of python or dash but here are my thoughts:

The call back appears to be inside the page2.layout, which should be to render html. What happens if you unindent it and call: from page 2 import * with page2 like above?

page2:

layout = html.Div([
                    dcc.Upload(
                    id='upload-data55',
                    children=html.Div([
                        'Drag and Drop or ',
                        html.A('Select File')
                    ]),
                    style={
                        'width': '100%',
                        'height': '60px',
                        'lineHeight': '60px',
                        'borderWidth': '1px',
                        'borderStyle': 'dashed',
                        'borderRadius': '5px',
                        'textAlign': 'center',
                        'margin': '10px'
                    },
                    multiple=False
                ),
                html.Div(id='output-data-upload55')
            ])
# unindented callback
@Dapp.callback(Output('output-data-upload55', 'children'),
                          [Input('upload-data55', 'contents'),
                           Input('upload-data55', 'filename'),
                           Input('upload-data55', 'last_modified')])
            def update_output(list_of_contents, list_of_names, list_of_dates):
                print('asd')
                return html.h1(list_of_contents+' '+list_of_names+' '+list_of_dates)

The constrain that you’re running into is that all callbacks need to be created before you run the app. To see what the problem is you need to think about how Python imports modules. Inside of main.py, Python will first import page2.py. Then in page2.py (I’m assuming that you have something like from main import Dapp at the top) it will then go and import the entire contents of main before it will load the rest of page2.py. That means the app will then be started before any callbacks from page2.py are run, meaning they won’t have any effect.

One solution is to move all your callbacks into the one main.py file (with layout attributes being fine to be left).

If you don’t like that option then you can get around the above limitation by structuring your app so that the Dash app instance is not in the same file that is the entry point to the code. A simple way to do this is define a server.py file that just defines your Dash and Flask instances. Then all of your other files (both main.py and page2.py) need to do from server import Dapp.

You can see examples of this approach in the code that runs the Dash Guide and in this project of mine here.

5 Likes

@nedned thank you for your comment! This was dumb but I hadn’t thought about it, and couldn’t understand why callbacks weren’t fired. You saved my day

Hi, I am trying to make an app where I am facing a similar issue, but when I move all the callbacks to the main.py and then have the layouts separately, I get errors saying that the callback is for an element id which does not exist. How can I resolve that ?

Hi, will this work if page1.py and page2.py have equally called component ids?
e.g.

page1.py

layout=html.Div(id=“div1”)

@callback to change div1 text

page2.py

layout=html.Div(id=“div1”)

@callback to change div1 text

In mine, when running main.py, I get an error, of 2 ids being called the same:
“Any given output can only have one callback that sets it.”

Because you are importing everything to main.py it acts as if they are all in the same single file. Just use different ids.

1 Like

I have been working on this issue more than 15 hours and your comment saved me. As you said I have created a file 'serverapp; and put those main 4 line in there and got rid of them in app file. Next I have put from serverapp import app. Everything went smooth. Thanks again!
PS. I have created an account here just to make this comment! :slight_smile:

1 Like

Hi,

Im having the same issue for 3 weeks, I cant understand what to do here? I have read all this and tried to solve it but im just not getting it… sorry…I tried the slap dash project but could not get that to work at all.

I tried taking just the callbacks out of the page files and into my index.py file but obviously you cant do that with the functions that are with those callbacks because the df and needed data for the functions are not in the main app file so that doesnt work… it errors for df… for example…

So when i try to just bring the callback in i get these errors below… screen shots… about layout and id’s…i have different id’s for each callback so that didnt work…

I tried to create a server.py file and put in it…app = dash.Dash(name)… and then tried to import that in the pages with from server import app but it wont let me do that… just errors lines under server?? Was that the right thing to put in it? how do i import it in all the other files?

Screen Shot 2021-08-24 at 3.46.03 PM

My versions:
Dash 1.20.0
Dcc version 1.16.0
im on a macbook pro: catalina, 10.15.5

I have a multi page app, with a basic app.py file as follows:

import dash
import dash_auth

app = dash.Dash(__name__, suppress_callback_exceptions=True,
                meta_tags=[{'name': 'viewport',
                            'content': 'width=device-width, initial-scale=1.0'}],

                )

server = app.server

then I have an index.py which is the main page I guess you would call it… its as follows…

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import pandas as pd
import plotly.express as px

# Connect to main app.py file
from app import app
from app import server

# Connect to your app pages  (add pages here from apps file, py files)
from apps import vgames, global_sales, macro, card, esg, nlp, home, live, alpaca, nlpdial, economic, news, socialdata, energy, data


app = dash.Dash(__name__)
app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    dbc.NavbarSimple(
        children=[

            dbc.NavLink("Home", href="/", active="exact"),
            dbc.NavLink("About", href="apps/home", active="exact"),

            dbc.DropdownMenu(
                [dbc.DropdownMenuItem("DEF",
                                      href="apps/vgames", active="exact"),
                 
                 ],
                label="Crypto",
                nav=True,),

            dbc.DropdownMenu(
                [dbc.DropdownMenuItem("Port",
                                      href="apps/global_sales", active="exact"),
                
                                   
                label="Research",
                nav=True,),


            dbc.DropdownMenu(
                [dbc.DropdownMenuItem("Data Points",
                                      href="apps/macro", active="exact"),
             
                 ],
                label="Mr",
                nav=True,),


            dbc.DropdownMenu(
                [dbc.DropdownMenuItem("NL",
                                      href="apps/nlp", active="exact"),
                 dbc.DropdownMenuItem("NLS",
                                      href="apps/nlpdial", active="exact"),
                 dbc.DropdownMenuItem("Calendar",
                                      href="https://www.facebook.com/profile.php?id=100070143875491",
                                      external_link=False,),

                 ],
                label="NLP",
                nav=True,),

            dbc.NavLink("Charts", href="apps/live",
                        active="exact", external_link=True),
            dbc.NavLink("Charts1", href="apps/alpaca",
                        active="exact", external_link=True),

            dbc.NavLink("NEWS",
                        href="apps/news", active="exact"),

            dbc.NavLink("Media Data",
                        href="apps/socialdata", active="exact"),

            dbc.DropdownMenu(
                [dbc.DropdownMenuItem("Twitter", "External relative",
                                      href="https://twitter.com/bull_swap",
                                      external_link=True,),
               
                 ],
                label="Links",
                nav=True,)



        ],
        brand="Research & Analytics",
        color="primary",
        dark=True,
    ),
    dbc.Container(id="page-content", className="pt-4"),

   
@ app.callback(Output(component_id='page-content', component_property='children'),
               [Input(component_id='url', component_property='pathname')])
def display_page(pathname):
    if pathname == '/apps/vgames':
        return vgames.layout
    if pathname == '/apps/global_sales':
        return global_sales.layout
    if pathname == '/apps/macro':
        return macro.layout
    if pathname == '/apps/card':
        return card.layout
    if pathname == '/apps/esg':
        return esg.layout
    if pathname == '/apps/nlp':
        return nlp.layout
    if pathname == '/apps/home':
        return home.layout
    if pathname == '/apps/live':
        return live.layout
    if pathname == '/apps/alpaca':
        return alpaca.layout
    if pathname == '/apps/nlpdial':
        return nlpdial.layout
    if pathname == '/apps/economic':
        return economic.layout
    if pathname == '/apps/news':
        return news.layout
    if pathname == '/apps/socialdata':
        return socialdata.layout
    if pathname == '/apps/energy':
        return energy.layout
    if pathname == '/apps/data':
        return data.layout
    else:
        return home.layout




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

I cut out a lot of the repeat pages in the drop downs for simplicity here but u can see the structure in the following:

def display_page(pathname):
if pathname == ‘/apps/vgames’:
return vgames.layout
if pathname == ‘/apps/global_sales’:
return global_sales.layout

I have multiple pages with callbacks that are supposed to update with data/prices from api’s but none load the charts… only empty charts

this is one of the pages for example… live.py.

import pandas as pd
import plotly
import plotly.express as px
from app import app
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from dash.exceptions import PreventUpdate
import dash_bootstrap_components as dbc
import pandas_datareader.data as web
import datetime

from alpha_vantage.timeseries import TimeSeries

key = 'key_here'  # Your API Key

ts = TimeSeries(key, output_format='pandas')  # 'pandas' or 'json' or 'csv'

# -------------------------------------------------------------------------------
# (only for example purposes) Pull data from API and prepare it for plotting on line chart

# Get the data, returns a tuple
# ttm_data is a pandas dataframe, ttm_meta_data is a dict
# https://github.com/RomelTorres/alpha_vantage/blob/develop/alpha_vantage/timeseries.py
# tsla_data, tsla_meta_data = ts.get_intraday(
#    symbol='TSLA', interval='1min', outputsize='compact')
# print(tsla_meta_data)

# df = tsla_data.copy()
# print(df.head())

# df = df.transpose()
# print(df.head())

# df.rename(index={"1. open": "open", "2. high": "high", "3. low": "low",
#                "4. close": "close", "5. volume": "volume"}, inplace=True)
# df = df.reset_index().rename(columns={'index': 'indicator'})
# print(df.head())

# df = pd.melt(df, id_vars=['indicator'], var_name='date', value_name='rate')
# df = df[df['indicator'] != 'volume']
# print(df[:15])

# df.to_csv("data2.csv", index=False)
# exit()

layout = dbc.Container([   
    dbc.Row([
        dcc.Interval(
            id='my_interval',
            n_intervals=0,       # number of times the interval was activated
            interval=120*1000,   # update every 2 minutes
        ),

      
        dcc.Graph(id="world_finance"),



    ], className='mb-15'),

    dbc.Row([

        dcc.Markdown(children=markdown_text, style={
            'textAlign': 'center'}),
    ], className='fixed-bottom'),
    # dcc.Interval(id='update', n_intervals=0, interval=1000*5)


])

@app.callback(
    Output(component_id='world_finance', component_property='figure'),
    [Input(component_id='my_interval', component_property='n_intervals')]
)
def update_graph(n):
    """Pull financial data from Alpha Vantage and update graph every 2 minutes"""

    tsla_data, tsla_meta_data = ts.get_intraday(
        symbol='TSLA', interval='1min', outputsize='compact')
    df = tsla_data.copy()
    df = df.transpose()
    df.rename(index={"1. open": "open", "2. high": "high", "3. low": "low",
                     "4. close": "close", "5. volume": "volume"}, inplace=True)
    df = df.reset_index().rename(columns={'index': 'indicator'})
    df = pd.melt(df, id_vars=['indicator'],
                 var_name='date', value_name='rate')
    df = df[df['indicator'] != 'volume']
    print(df[:15])

    line_chart = px.line(
        data_frame=df,
        x='date',
        y='rate',
        color='indicator',

        title="Stock: {}".format(tsla_meta_data['2. Symbol'])
    )
    return (line_chart)


I made sure the… Input(component_id=‘my_interval’ .)]… id was different for each one of the callbacks i put in the index.py… but it still gave me the errors id not found in layout?

Sorry for the long post but i cant figure out how to say this with out all this information.

If the solution is to create new server.py file, can someone say exactly what goes in it? and then if i need to import that file into all the other files? and how?
If the solution is put all the callbacks in the index.py file, where exactly? Is there an explanation as to why it didnt work when i tried before? is there an import i need to do then?
THX

Hi, I fixed/solved the problems. I had mistakenly put /had an addtional
app = dash.Dash(name, suppress_callback_exceptions=True) in my index.py… when removed the callbacks worked… thx

1 Like