Dash in multi-user environment and objects

Hello Dash Community!

this is my first post in this forum : )
I’ve been using dash for a few months now and have read myself into Dash and tried out several things.

What I still don’t really understand is how to handle callbacks with lots of inputs and outputs.
In addition, there is the limitation that an element can be only once in a callback output. I know one solution
is the MultiplexerTransform from the dash-extensions library.
But anyhow as the code size grows my app gets difficult to maintain.
Therefore to organize my code more efficiently I would like to use classes to preprocess the data and use the callbacks only for trigger and output.
I created the following MWE and would like to know, if this could break in a multi-user environment, e.g. object attributes shared between users unintenionally?

main.py:

from dash import Dash
import mwe_component
from mwe_class import User_Data

app = Dash(__name__)
app.layout = mwe_component.render(app, User_Data())
app.run(debug=True)

mwe_component.py:

from dash import html, Input, Output, Dash


def render(app:Dash, obj):
    @app.callback(Output("lbl", "children"), Input("btn", "n_clicks"))
    def update(click):
        obj.click = click
        return obj.click

    return html.Div([
        html.Label(children="btn_click", id="lbl"),
        html.Button(children="Button", id="btn"),
    ])

mwe_class.py:

class User_Data():
    def __init__(self) -> None:
        self._click = None

    @property
    def click(self):
        return self._click

    @click.setter 
    def click(self,value):
        self._click = value

By when could this approach be dangerous to be unintentionally modification of attributes?

Thank you for your support!
Chris

Hello @chris80,

Welcome to the community!

So, the main reason you are wanting to use classes is because of the limitation of the one callback per output? - This is actually about to change, plotly has been working on removing this limitation. :smiley:

As far as rendering layouts specific to users, you can do all of that also without classes. You’ll need to have some way to get the users that are logged in. I use flask_login for this.

Now, with the layouts and criss-crossing info, if you use global variables, yes, it is very easy to mess it up. If you avoid those, then the app already operates in a stateless manner, thus keep them separated.

3 Likes

Hey jinnyzor,
thank you very much for your answer.
Actually I have several input fields which needs calculation and cascading to other elements.
The app will be used locally by few people on a server.
When using my example code, will users get their own class object instance, or is it shared?

In the documentation is an example for a not reliably working code. Has my code the same flaws even if not explicitly using the “global” flag in the callback?

df = pd.DataFrame({
  'student_id' : range(1, 11),
  'score' : [1, 5, 2, 5, 2, 3, 1, 5, 1, 5]
})

app.layout = html.Div([
	dcc.Dropdown(list(range(1, 6)), 1, id='score'),
	'was scored by this many students:',
	html.Div(id='output'),
])

@app.callback(Output('output', 'children'), Input('score', 'value'))
def update_output(value):
	global df
	df = df[df['score'] == value]
	return len(df)

the documentation then says:
The callback returns the correct output the very first time it is called, but once the global df variable is modified, any subsequent callback that uses that dataframe is not using the original data anymore.

To improve this app, reassign the filtered dataframe to a new variable inside the callback as shown below, or follow one of the strategies outlined in the next parts of this guide.

I would say that you could run into some unexpected behavior by setting df’s to be a df variable that is from the global scope.
This is actually still available without declaring the global variable.

If you wanted to keep the df as a base point that never changes, you can do something like this:

dff = df[df['score' == value]]

Then reference dff in the local scope.

I believe that they would get their own, but I dont think it would scale properly when the backend is spread out.

Hey @jinnyzor,

after carefully reading several posts together with your information I have now a better understanding about avoiding global variables.
Thank you again!

2 Likes