Structuring a multi-page app


#1

So I’d like to build a multi-page app where each page’s code is saved in its own file. I’ve attempted to follow the demo provided in the documentation, but it failed for me: https://dash.plot.ly/urls

Is this demo 100%? It seems like maybe the app.py example is missing something. I’m attempting to troubleshoot on my own, but wanted to know if others have successfully replicated this demo. Thanks!


#2

I modified the demo provided in the Dash documentation listed above, and successfully got it to work. It was necessary to include all of the callbacks in the primary file (app.py) – not sure if this is a best practice, but I couldn’t get it to work otherwise.

File structure is as follows:

main.py
pages folder:
— empty file named “_ _ init _ _.py”
— page_1.py
— page_2.py
— index.py

main.py

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from pages import index
from pages import page_1
from pages import page_2

print(dcc.__version__) # 0.6.0 or above is required

app = dash.Dash()

app.config.suppress_callback_exceptions = True

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

# Page 1 callback
@app.callback(dash.dependencies.Output('page-1-content', 'children'),
              [dash.dependencies.Input('page-1-dropdown', 'value')])
def page_1_dropdown(value):
    return 'You have selected "{}"'.format(value)

# Page 2
@app.callback(Output('page-2-content', 'children'),
              [Input('page-2-radios', 'value')])
def page_2_radios(value):
    return 'You have selected "{}"'.format(value)

# Index Page callback
@app.callback(Output('page-content', 'children'),
              [Input('url', 'pathname')])
def display_page(pathname):
    if pathname == '/page-1':
        return page_1.page_1_layout
    elif pathname == '/page-2':
        return page_2.page_2_layout
    else:
        return '404'

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

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

page_1.py

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

page_1_layout = html.Div([
    html.H1('Page 1'),
    dcc.Dropdown(
        id='page-1-dropdown',
        options=[{'label': i, 'value': i} for i in ['LA', 'NYC', 'MTL']],
        value='LA'
    ),
    html.Div(id='page-1-content'),
    html.Br(),
    dcc.Link('Go to Page 2', href='/page-2'),
    html.Br(),
    dcc.Link('Go back to home', href='/'),

])

page_2.py

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

page_2_layout = html.Div([
    html.H1('Page 2'),
    dcc.RadioItems(
        id='page-2-radios',
        options=[{'label': i, 'value': i} for i in ['Orange', 'Blue', 'Red']],
        value='Orange'
    ),
    html.Div(id='page-2-content'),
    html.Br(),
    dcc.Link('Go to Page 1', href='/page-1'),
    html.Br(),
    dcc.Link('Go back to home', href='/')
])

index.py

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

index_page = html.Div([
    dcc.Link('Go to Page 1', href='/page-1'),
    html.Br(),
    dcc.Link('Go to Page 2', href='/page-2'),
])

#3

here is multipage app organized 1page=1file callbacks are placed inside this files + custom css. it can be usefull as template to start with


#4

Thank you for this! How do you serve it? gunicorn? Does it work with multiple workers?


#5

I use it with nginx as proxy + gunicorn, but nginx is not necessary. Yes it works with multiple workers.


#6

Hi Roman,
THx for sharing.
Is the link still active?
It does not work for me.


#7

updated link https://yadi.sk/d/JnM7BvKbJp3EdA


#8

Thank you very much @roman !