This is a great question. Here’s my strategy for breaking out a Dash app into
manageable pieces:
I have a directory structure as follows:
run.py
myproject/
__init__.py
app.py
server.py
callbacks1.py
callbacks2.py
wsgi.py
__init__.py
empty file
app.py
from .server import app, server
from . import callbacks1
from . import callbacks2
app.layout = html.Div('hello world')
server.py
from flask import Flask
from dash import Dash
server = Flask('myproject')
app = Dash(server=server)
callbacks1.py
from .server import app
@app.callback(...)
def callback1(...):
pass
callbacks2.py
from .server import app
@app.callback(...)
def callback2(...):
pass
wsgi.py
from .app import server as application
run.py
from myproject.app import app
if __name__ == '__main__':
app.run_server()
In this setup, the Dash app is organised into a package called myproject
,
which has a module app.py
, which is the entry point to the app that imports
everything else. The actual Dash instance (app
) itself is found in the server.py
module along with the Flask instance (server
). The reason for this perhaps
confusing separation will become clear shortly.
The callbacks are defined in callbacks1.py
and callbacks2.py
and each need
to be imported in app.py
so that they are registered with the Dash
instance. At the top of each of the callback modules, we need to import the Dash
instance from the server
module. This is why we have to use separate app
and
server
modules: the app
module imports each callback module, but if each
callback module immediately tried to import from the app
module, we’d have a
circular dependency and the import chain would fail. So we break them up to
remove the circularity.
As for running the app, because it’s a package, we can’t use the
if __name__ == '__main__':
entry point to run the server. So we can either use
the WSGI entry point with a WSGI server like gunicorn :
$ gunicorn myproject.wsgi
Or you can use the run.py script that uses the Flask server:
$ run.py
Both those commands assume you’re in the same directory as the myproject
directory. But we could also take things to the next level and make this package
installable by creating a setup.py
file at the top level of the project. Then, once installed,
the gunicorn command can be run from any directy (assuming the virtualenv you
installed your project has gunicorn installed and is activated).
Hopefully that all makes sense!