Deployment on Heroku with dash app as class instantiation?

Hello everyone,
I have successfully deployed my dash app on Heroku following the standard method:

  1. app.py contains all the commands, including:

app = dash.Dash(…)
server = app.server

if name == ‘main’:
app.run_server(…)

  1. The Procfile contains just one line: “web: gunicorn app:server”.

Everything works well, except now I want to modify variables dynamically etc, so I built a class DashApp which defines the data generation, all the variables, the layout, and the callbacks separately. In a nutshell, my code now looks like this:


class DashApp:
     def __init__(self):
            #definitions of all variables etc.
            self.myvar = blabla 

            #definitions of the app & layout:
            self.app = dash.Dash(some_options)
            self.app.layout = self.create_layout()
            self.app.callback(Outputs, Inputs)(self.update_data)

    # various functions that do various things, including:
    def create_layout():
           blabla

    def update_data():
           blabla

------------------------
if __name__ == '__main__':
    app = DashApp()
    app.app.run_server(some_options)
------------------------

I basically just put the definition of the app into a class and instantiate the class to run the app. If I run this locally directly with python, everything runs smoothly, including the callbacks. Now, if I want to deploy this on Heroku, I need to correctly define the server attribute contained in the Procfile. How does that work? I tried different approaches, but none of them work as I wish:

  1. If I define it as usual with “server = app.app.server” after the app instantiation (below app = DashApp(), after the if-statement), I get the error “Failed to find attribute ‘server’ in ‘app’”.
  2. To avoid this error, I tried to put the instantiation app = DashApp() before the if-statement (making it an attribute of the module) and added server = app.server right after. This avoids the error of point 1) and the app loads, but the callbacks stop working.
  3. I tried to put the server definition in the init function of the class, but that doesn’t work either (same error as 1)).
  4. I also tried to modify the Procfile to “web: gunicorn app.app:server” and “web: gunicorn app:app.server” but neither works.

Does anybody know a workaround for this? It would be much appreciated! I’ve been struggling the whole day :frowning:

I believe it should work, if you make the app initialisation out of the main guard, and bind the server variable just after. Something like this,

app = DashApp()
server = app.app.server

if __name__ == '__main__':
    app.app.run_server(some_options)

That being said, could you elaborate on what you are trying to achieve using the class structure?

Thanks @Emil, I had tried with this method before but the problem is that the callback functions defined in the class do not work correctly anymore. For instance, when I change a date picker in the app to change a graph, the corresponding callback is not triggered unless I refresh the page.

The reason why I was using the class is because I have several variables that I want to change dynamically with callbacks (e.g. changing the date picker to change which .csv file is imported and shown in the graphs) and I thought making them class attributes is cleaner than having them as global attributes of the module.

That design is not really compatible with Dash. As noted in the docs,

the use of global variables is discouraged. And using class variables this way falls under the same clause. In essence, the Dash server is designed to be kept stateless. So any state (i.e. your class variables) must be stored elsewhere, either client side (e.g. in a Store component) or server side (e.g. in a file or a shared cache). The former is the easiest to setup, the latter will typically be more performant for larger amounts of data.

1 Like

I guess I will just have to revert back to the classless construction and use Store then. Thanks @Emil!