✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🧬 Learn how to build RNA-Seq data apps with Python & Dash. Register for the May 20 Webinar!

Show and Tell: Dash Multipage structure w/ individual folders for apps and automated importing/routing

Hi All,

I’ve been using dash for a few years to Dashboard data (and trying to pickup python along the way!). I have a server that is serving dozens of individual ‘apps’ from a single index (as shown in the example see URL Routing and Multiple Apps | Dash for Python Documentation | Plotly under “Structuring a Multi-Page App” section). The general structure is like this example:

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

and using the index example:

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


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':
        return app1.layout
    elif pathname == '/apps/app2':
        return app2.layout
    else:
        return '404'

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

While this structure worked well as I was starting, now that I have ballooned to dozens of apps, maintaining is getting kind of tedious. If I add a new app, I have to update the index.py import list and the layout routing/handling. Also, if a single app isn’t working, the whole server won’t load. The local data sources have also gotten very ugly as there is no folders tied to each app.

I want to restructure the dashboard and could use some advice/feedback on this structure. Some things I want from the new structure:

  • Each app will have its own folder, to hold dashboard and data files
  • Layout and callbacks will be in separate files in their app folder
  • individual app folder with layout/callbacks/data files will ignore absolute path (can be plugged into the apps folder from a development location without modification)
  • If an app fails to load, the server will still function normally
  • An app folder can be labeled to ignore it from loading
  • The index.py will never need to be changed when adding/removing apps, and can have a defined ‘default’ layout and a 404 layout

The overall structure i have devised looks like this:

Here is a repository where I have built a proof-of-concept:
GitHub - ickL/DashMultiPageExample: An example of a multipage dash app with individual folders and separated layouts/callbacks/data folders. (first time sharing a git repository, hopefully it is complete enough to understand!)

Has anyone else attempted anything like this? Am i wandering into some novice coding pitfall with this design? Thank you!

1 Like

I have a started kit that may resolve the issue you are experience. It will atleast make the code easier to reason about.

Ping me if you need more info on the starter kit.

Hi @jnguyen0220, thanks for the suggestion. I see the layouts/callbacks in the structure you propose are not separated into individual files, nor are they in independent directories. Also, the layout logic appears to need to be updated when adding new layouts to your ‘router.py’ file. My goal was to make a server that only requires someone to drop in a self-contained app folder and the server will serve up the new app after a reboot, maybe a little different than your goals.

Wrote lots of stuff above without having looked carefully at your code :sweat_smile:

I think you’re on the right path, the main things I would do differently is have a config file with all the apps I want to import and have my apps completely separated from the main repo (have them as standalones) and use werkzeug.wsgi.DispatcherMiddleware to connect them, but that’s more of a preference. If you mean to use this for yourself then that’s fine. Creating a library that’s generalized for others to use (not a template/POC to adapt but a library) might be a lot harder though.

Again, I think you’re on the right track.

1 Like