Access data in callback definition

TL;DR: How to access data/state in the callback definitions (e.g. Input(...) or State(...)) ?

Consider the following dummy example:

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

df = pd.DataFrame(
    {
        "user": ["a", "b", "c", "d"],
        "data": [1, 5, 6, 3],
        "category": ["x", "x", "y", "y"],
    }
)

app = dash.Dash(__name__)
app.config.suppress_callback_exceptions = True

app.layout = html.Div(
    [
        dcc.Store("users"),
        dcc.Dropdown(
            id="dropdown",
            options=[{"label": cat, "value": cat} for cat in ["x", "y"]],
            value="x",
        ),
        html.Div(id="buttons-container"),
        html.Div(id="output-text"),
    ]
)


@app.callback(Output("users", "data"), [Input("dropdown", "value")])
def get_users(cat):
    if cat:
        return df.query("category == @cat")["user"].to_list()
    else:
        return None


@app.callback(Output("buttons-container", "children"), [Input("users", "data")])
def create_buttons(users):
    if users:
        return [html.Button(f"btn-{user}", id=f"btn-{user}") for user in users]
    else:
        return None


@app.callback(
    Output("output-text", "children"),
    [Input(f"btn-{user}", "n_clicks") for user in **NEED USERS.DATA HERE**],
)
def update_output(*args):
    user = dash.callback_context.triggered[0]["prop_id"].split(".")[0].split("-")[-1]
    return f"You clicked user {user} button"


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

In this app,

  1. a dataframe df contains one row for each user with an associated category x or y
  2. the get_users callback store the list of users that belong to the category selected in the dropdown
  3. from this list of users, the create_buttons callback create one button for each user
  4. in the update_output, I want to connect each visible buttons to an output text. However, I need to access data during the callback definitions (Input(...)) to know the id of the buttons.

I have read the proposed solutions in Dynamic Controls and Dynamic Output Components. But in real life, my application is connected with a Google Sheet. So I don’t know in advance the users and categories.

I tried several things without success. Do you have a solution or ideas to avoid this problem?

What a i did to solve a similar problems is to create a generic function that creates callbacks in the moment based on what the dataframe has using as an advantage the functools.partial function from python to ensure flexibility on the callbacks. You can call it before you create the buttons since you know the id of the element in that moment the problem as i see it is that you cannot create 2 callbacks for the same button in case you change the category selection for another search so the funcion that creates callbacks has to verify id the element existed previously an thats an issue since you would have to check every time for the error that dash will throw of not allowing you to have 2 callbacks for the same output. Besides the number of callbacks will grow as the google sheet does and that is gonna make you app runs slow. maybe there is another approach that lets you make something else? whats is really the main purpose of the buttons? redirect to other pages ?? do some stuff in your database?

1 Like

Thank you for your answer.

Here is an example:

Here, each user has their own card with associated buttons (in and out). By clicking on a button, a dialog appears and the user can add data to the database for the user linked to the button.
I’m not too worry about performance as I will not have more than 50 users.

I found a solution by declaring my dataframe global and creating all my elements (e.g. buttons) during the creation of the application and hide the one I don’t use (with style = {'display': 'none'}). Even if it works, it’s a hack because the dataframe is global and a large number of callbacks are created (about a hundred).

Dash is a wonderful framework, but I’m considering moving to a pure Flask app for this particular case.

Sorry i couldn’t see your response until now yeah i think dash may not be the most easy way to work around this and global dataframes may not be the most recommendable approach. if you find hosting another web app only for this consider mounting you dash application inside Flask