Why Prevent_initial_call does not stop the initial call?

I have the written code below. I have two dropdown menus that work based on chained callbacks. the first dropdown menu gets the datasets and reads the columns’ names and updates the options in the second dropdown menu. Then, the parameters can be plotted on the chart. my dataframes look like this:

df={'col1':[12,15,25,33,26,33,39,17,28,25],
    'col2':[35,33,37,36,36,26,31,21,15,29],
    'col3':['A','A','A','A','B','B','B','B','B','B'],
    'col4':[1,2,3,4,5,6,7,8,9,10]

I want to highlight the chart background depending on the categories in col3. I don’t understand why when I select the dataset from the first dropdown menu the background color for col3 appears on the chart (before selecting the parameters). I have used Prevent_initial_call = True, but the second callback still triggers.

import dash
from dash import Dash, html, dcc, Output, Input, State, MATCH, ALL
import plotly.express as px
import pandas as pd
import numpy as np
import dash_bootstrap_components as dbc

app = Dash(__name__)

app.layout = html.Div([
    html.Div(children=[
        html.Button('add Chart', id='add-chart', n_clicks=0)
    ]),
    html.Div(id='container', children=[])
])

@app.callback(
    Output('container', 'children'),
    [Input('add-chart', 'n_clicks'),
    Input({'type': 'remove-btn', 'index': ALL}, 'n_clicks')],
    [State('container', 'children')],
    prevent_initial_call=True
)
def display_graphs(n_clicks, n, div_children):

    ctx = dash.callback_context
    triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]

    elm_in_div = len(div_children)

    if triggered_id == 'add-chart':
        new_child = html.Div(
            id={'type': 'div-num', 'index': elm_in_div},
            style={'width': '25%',
                   'display': 'inline-block',
                   'outline': 'none',
                   'padding': 5},
            children=[
                dbc.Container([
                    dbc.Row([
                        dbc.Col([dcc.Dropdown(id={'type': 'dataset-choice', 'index': n_clicks},
                                        options=['dataset1'],
                                        clearable=True,
                                        value=[]
                                )], width=6),
                        dbc.Col([dcc.Dropdown(id={'type': 'feature-choice', 'index': n_clicks},
                                              options=[],
                                              multi=True,
                                              clearable=True,
                                              value=[]
                                )], width=6)
                    ]),
                    dbc.Row([
                        dbc.Col([dcc.Graph(id={'type': 'dynamic-graph','index': n_clicks},
                                           figure={}
                                )])
                    ]),
                    dbc.Row([
                        dbc.Col([html.Button("Remove", id={'type': 'remove-btn', 'index': elm_in_div})
                        ])
                    ]),
                ])
            ]
        )
        div_children.append(new_child)
        return div_children

    if triggered_id != 'add-chart':
        for idx, val in enumerate(n):
            if val is not None:
                del div_children[idx]
                return div_children


@app.callback(
    Output({'type': 'feature-choice', 'index': MATCH}, 'options'),
    [Input({'type': 'dataset-choice', 'index': MATCH}, 'value')],
    prevent_initial_call=True
)
def set_dataset_options(chosen_dataset):
    if chosen_dataset is None:
        return dash.no_update
    else:
        path = 'C:/Users/pymnb/OneDrive/Desktop/test/'
        df = pd.read_csv(path + chosen_dataset+'.csv')
        features = df.columns.values[0:2]
        return features


@app.callback(
    Output({'type': 'dynamic-graph', 'index': MATCH}, 'figure'),
    [Input({'type': 'dataset-choice', 'index': MATCH}, 'value'),
     Input({'type': 'feature-choice', 'index': MATCH}, 'value')],
    prevent_initial_call=True
)
def update_graph(chosen_dataset1, chosen_feature):
    if chosen_feature is None:
        return dash.no_update
    if chosen_dataset1 is None:
        return dash.no_update

    path = 'C:/Users/pymnb/OneDrive/Desktop/test/'
    df = pd.read_csv(path + chosen_dataset1+'.csv')

    Xmin = df[chosen_feature].min().min()
    print(Xmin)
    Xmax = df[chosen_feature].max().max()

    # to find the height of y-axis(col4)
    col4_max = df['col4'].max()
    col4_min = df['col4'].min()

    fig1 = px.line(df, x=chosen_feature, y='col4')
    fig1.update_layout({'height': 600,
                'legend': {'title': '', 'x': 0, 'y': 1.06, 'orientation': 'h'},
                'margin': {'l': 0, 'r': 20, 't': 50, 'b': 0},
                'paper_bgcolor': 'black',
                'plot_bgcolor': 'white',
                }
    )

    fig1.update_yaxes(range=[col4_max, col4_min], showgrid=False)
    fig1.update_xaxes(showgrid=False)

    categ_col3 = df.col3.dropna().unique()
    colors = ['#54FF9F', '#87CEFF']

    for (i,j) in zip(categ_col3, colors):

        index_min = df.loc[df.col3 == i].index[0]
        index_max = df.loc[df.col3 == i].index[-1]
        if index_min == 0:
            cat_min = df['col4'][index_min]
        else:
            cat_min = df['col4'][index_min-1]
        cat_max = df['col4'][index_max]

        fig1.add_shape(type="rect", x0=Xmin, y0=cat_min, x1=Xmax, y1=cat_max,
                       fillcolor=j, layer='below', opacity=0.5,
                       )
    return fig1

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

Hello @Peyman,

Prevent_Intial_Call only stops the first call after rendering. Once you start interacting with the site, the callbacks will triggers.

A way to keep this from happening is adding if statements to check if there is data to be processed by the following callbacks.

Oh, I see. Sorry, I am a little new to plotly dash. So, everything after the first callback, and its function, go inside the if statement?
Is that possible to show me how I can do that in the code?

Sure thing, here is the result:

Code (I had to remove your coding for the location obviously.

import dash
from dash import Dash, html, dcc, Output, Input, State, MATCH, ALL
from dash.exceptions import PreventUpdate
import plotly.express as px
import pandas as pd
import numpy as np
import dash_bootstrap_components as dbc

app = Dash(__name__)

app.layout = html.Div([
    html.Div(children=[
        html.Button('add Chart', id='add-chart', n_clicks=0)
    ]),
    html.Div(id='container', children=[])
])

df=pd.DataFrame({'col1':[12,15,25,33,26,33,39,17,28,25],
    'col2':[35,33,37,36,36,26,31,21,15,29],
    'col3':['A','A','A','A','B','B','B','B','B','B'],
    'col4':[1,2,3,4,5,6,7,8,9,10]
    })

@app.callback(
    Output('container', 'children'),
    [Input('add-chart', 'n_clicks'),
    Input({'type': 'remove-btn', 'index': ALL}, 'n_clicks')],
    [State('container', 'children')],
    prevent_initial_call=True
)
def display_graphs(n_clicks, n, div_children):

    ctx = dash.callback_context
    triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]

    elm_in_div = len(div_children)

    if triggered_id == 'add-chart':
        new_child = html.Div(
            id={'type': 'div-num', 'index': elm_in_div},
            style={'width': '25%',
                   'display': 'inline-block',
                   'outline': 'none',
                   'padding': 5},
            children=[
                dbc.Container([
                    dbc.Row([
                        dbc.Col([dcc.Dropdown(id={'type': 'dataset-choice', 'index': n_clicks},
                                        options=['dataset1'],
                                        clearable=True,
                                        value=[]
                                )], width=6),
                        dbc.Col([dcc.Dropdown(id={'type': 'feature-choice', 'index': n_clicks},
                                              options=[],
                                              multi=True,
                                              clearable=True,
                                              value=[]
                                )], width=6)
                    ]),
                    dbc.Row([
                        dbc.Col([dcc.Graph(id={'type': 'dynamic-graph','index': n_clicks},
                                           figure={}
                                )])
                    ]),
                    dbc.Row([
                        dbc.Col([html.Button("Remove", id={'type': 'remove-btn', 'index': elm_in_div})
                        ])
                    ]),
                ])
            ]
        )
        div_children.append(new_child)
        return div_children

    if triggered_id != 'add-chart':
        for idx, val in enumerate(n):
            if val is not None:
                del div_children[idx]
                return div_children


@app.callback(
    Output({'type': 'feature-choice', 'index': MATCH}, 'options'),
    [Input({'type': 'dataset-choice', 'index': MATCH}, 'value')],
    prevent_initial_call=True
)
def set_dataset_options(chosen_dataset):
    if chosen_dataset is None:
        return dash.no_update
    else:
        features = df.columns.values[0:2]
        return features


@app.callback(
    Output({'type': 'dynamic-graph', 'index': MATCH}, 'figure'),
    [Input({'type': 'dataset-choice', 'index': MATCH}, 'value'),
     Input({'type': 'feature-choice', 'index': MATCH}, 'value')],
    prevent_initial_call=True
)
def update_graph(chosen_dataset1, chosen_feature):
    if chosen_feature and chosen_dataset1:

        Xmin = df[chosen_feature].min().min()
        print(Xmin)
        Xmax = df[chosen_feature].max().max()

        # to find the height of y-axis(col4)
        col4_max = df['col4'].max()
        col4_min = df['col4'].min()

        fig1 = px.line(df, x=chosen_feature, y='col4')
        fig1.update_layout({'height': 600,
                    'legend': {'title': '', 'x': 0, 'y': 1.06, 'orientation': 'h'},
                    'margin': {'l': 0, 'r': 20, 't': 50, 'b': 0},
                    'paper_bgcolor': 'black',
                    'plot_bgcolor': 'white',
                    }
        )

        fig1.update_yaxes(range=[col4_max, col4_min], showgrid=False)
        fig1.update_xaxes(showgrid=False)

        categ_col3 = df.col3.dropna().unique()
        colors = ['#54FF9F', '#87CEFF']

        for (i,j) in zip(categ_col3, colors):

            index_min = df.loc[df.col3 == i].index[0]
            index_max = df.loc[df.col3 == i].index[-1]
            if index_min == 0:
                cat_min = df['col4'][index_min]
            else:
                cat_min = df['col4'][index_min-1]
            cat_max = df['col4'][index_max]

            fig1.add_shape(type="rect", x0=Xmin, y0=cat_min, x1=Xmax, y1=cat_max,
                           fillcolor=j, layer='below', opacity=0.5,
                           )
        return fig1
    return px.line()

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