My First webapp on Dash. My misunderstanding about callback

Hi, I’ve been using Dash on Python for a week now to develop a Webapp that may seem simple but for which I can’t find an answer to my dysfunction. :roll_eyes:
However, I made some interesting points.

  • having a dataset displayed with the possibility of being able to filter on the fly on the values: DONE

  • Be able to export the filtered dataset in csv: DONE

  • To be able to connect my dropdown to filter on fixed variables: NOT OK

However, it seems to be the simplest thing, but I really don’t understand how the relation of the callback works with the function of updating the dataset via the “children”.

Thanks to anyone who could help me in my experience with this framework. :slight_smile:
Here is the structure of my code stripped to the maximum of unnecessary information.

app.layout = html.Div([


html.H1("Global Referential of Network Products"),
#SELECT COUNTRY 


#DATASET INTERACTIF
 html.Div([
    html.Label('Quick research input dataset'),
    dash_table.DataTable(
        id='datatable_id',
        data=df.to_dict('records'),
        columns=[
            {"name": i, "id": i, "deletable": False, "selectable": False} for i in df.columns
        ],
        fixed_rows={ 'headers': True, 'data': 0 },
        filter_action="native",
        sort_action="native",
        row_deletable=False,
        page_action="native",
        page_current= 0,
        page_size= 10,
        virtualization=False,
        style_cell_conditional=[
            {'if': {'Country': 'Country'},
             'width': '30%', 'textAlign': 'left'},
            {'if': {'Manufacturer_Code': 'Manufacturer_Code'},
             'width': '30%', 'textAlign': 'left'},
            {'if': {'Local_Code': 'Local_Code'},
             'width': '30%', 'textAlign': 'left'},
            {'if': {'Item_Description': 'Item_Description'},
             'width': '10%', 'textAlign': 'left'},
        ],
        export_format='xlsx',
        export_headers='display',
        merge_duplicate_headers=True,            
    ),
     dcc.Dropdown(id="slct_country",
             options=[
                 {"label": country_list[0], "value": country_list[0]},
                 ...
                 {"label": country_list[8], "value": country_list[8]}],
             multi=False,
             value=country_list[2],
             style={'width': "60%"}
             ),
     html.Div([
     html.Button("Add Filter", id="add-filter", n_clicks=0),
     html.Div(id='dropdown-container', children=[]),
     html.Div(id='dropdown-container-output')
])
    ],className='row')
    
])


@app.callback(
    [Output(component_id='datatable_id', component_property='data'),
     Output(component_id='data_filtered', component_property='children'),
     Output('dropdown-container', 'children')],
    [Input(component_id='slct_country', component_property='value')])


def display_dropdowns(children):
    new_dropdown = dcc.Dropdown(
        country_list,
        id={
            'type': 'filter-dropdown',
            'index': n_clicks
        }
    )
    children.append(new_dropdown)
    return children



def update_data(country_value):
    if len(country_value)==0:
        df_filterd = dff[dff['Country'].isin(country_list)]
    else:
        print(country_value)
        df_filterd = dff[dff.index.isin(country_value)]
    return country_value

As you can see, I have in my Input component the dropdown select_country with the value as the country I selected into it but It had any impact in my dataset after.

HI @Grixis6

:wave: Welcome to the community.

That’s really nice code for just one week of using Dash.

First of all, you are declaring component_id='data_filtered' in the second output. Where is that ID coming from? it’s not in the layout or anywhere else.

Also, the best practice in writing callbacks is to use one callback decorator and one callback function (you have two functions). And for every output, you need to return an object. So if you have 3 outputs, you would need to return 3 objects.

I recommend reading over this basic callbacks chapter and letting us know if you have any questions.

Hello @adamschroeder ,

Thank you very much for the time you give me and sorry for my mistake I didnt clean my code very well before post it.
In my case I have 1 callback with 2 functions. I have 2 outputs with 2 objetcs to return.
As you can see is 2 DataTable. The first one is filtered by the dropdown about Country variable and the second one should be filtered by Manufactured Code.
But both of them don’t work.

app.layout = html.Div([

  

    #DATASET INTERACTIF
     html.Div([
        html.Label('Quick research input dataset'),
        dash_table.DataTable(
            id='datatable_id',
            data=df.to_dict('records'),
            row_deletable=True,
            columns=[
                {"name": i, "id": i, "deletable": False, "selectable": False} for i in df.columns
            ],
            fixed_rows={ 'headers': True, 'data': 0 },
            #editable=False,
            filter_action="native",
            sort_action="native",
            sort_mode="multi",
            #row_selectable="multi",
            #selected_rows=[],
            page_action="native",
            page_current= 0,
            page_size= 10,
            style_cell={
            'whiteSpace': 'normal'
            },
            virtualization=False,
            style_cell_conditional=[
                {'if': {'Country': 'Country'},
                 'width': '30%', 'textAlign': 'left'},
                {'if': {'Manufacturer_Code': 'Manufacturer_Code'},
                 'width': '30%', 'textAlign': 'left'},
                {'if': {'Local_Code': 'Local_Code'},
                 'width': '30%', 'textAlign': 'left'},
                {'if': {'Item_Description': 'Item_Description'},
                 'width': '10%', 'textAlign': 'left'},
            ],
            export_format='xlsx',
            export_headers='display',
            merge_duplicate_headers=False            
        ),
         dcc.Dropdown(id="slct_country",
                 options=[
                     {"label": country_list[0], "value": country_list[0]},
                        ...
                     {"label": country_list[8], "value": country_list[8]}],
                 multi=False,
                 value=country_list[0],
                 style={'width': "60%"}
                 ),
         
    ],className='row'),
    html.Div(
    children=[
        dcc.Dropdown(
            id="filter_dropdown",
            options=manucode_list,
            placeholder="-Select a Manu Code",
            multi=True
        )
        ],className='row'),

    html.Div([
        dash_table.DataTable(
            id="table-container",
            columns=[{"name": i, "id": i} for i in df.columns.values],

            data=df.to_dict("records"),
            
            row_deletable=True,
            fixed_rows={ 'headers': True, 'data': 0 },
            style_cell_conditional=[
                {'if': {'Country': 'Country'},
                 'width': '30%', 'textAlign': 'left'},
                {'if': {'Manufacturer_Code': 'Manufacturer_Code'},
                 'width': '30%', 'textAlign': 'left'},
                {'if': {'Local_Code': 'Local_Code'},
                 'width': '30%', 'textAlign': 'left'},
                {'if': {'Item_Description': 'Item_Description'},
                 'width': '10%', 'textAlign': 'left'},
            ],
            page_action="native",
            page_current= 0,
            page_size= 10,
            export_format='xlsx',
            export_headers='display',
            merge_duplicate_headers=False
            #virtualization = True
         )

    ],className='row'),
    
    
])



@app.callback(
    [Output(component_id='datatable_id', component_property='data'),
     Output("table-container", "data")
    ],
    [Input(component_id='slct_country', component_property='value'),
     Input("filter_dropdown", "value")
    ])


def display_table(manufacturer):
    dff = df[df.Manufacturer_Code.isin(manucode_list)]
    return dff.to_dict("records")

I think my weakness is at this point : def a iterative function with my callback

My guess of what it should be (I suppose about what I read on your link)

def display_table(update_my_first_table_by_country_slct):
    new_datatable= df[df.Country="value selected in my dropdown slcty_country"]
    return dff.to_dict("records")

Hi @Grixis6 . I’m also fairly new to Dash . Here are some thoughts that I think might be helpful:

For this snippet:

@app.callback(
    [Output(component_id='datatable_id', component_property='data'),
     Output("table-container", "data")
    ],
    [Input(component_id='slct_country', component_property='value'),
     Input("filter_dropdown", "value")
    ])


def display_table(manufacturer):
    dff = df[df.Manufacturer_Code.isin(manucode_list)]
    return dff.to_dict("records")

You have two Inputs but are only bringing one into the function. Also, the input ‘manufacturer’ isn’t used in the function definition. I think the callback and function should look something like this:

@app.callback(
    [Output(component_id='datatable_id', component_property='data')
    ],
    [Input(component_id='slct_country', component_property='value'),
     Input('filter_dropdown', "value")
    ])


def display_table(country_input, manufacturer_input):
   df = df[df.Country == country_input]
   dff = df[df.Manufacturer_Code == manufacturer_input]

   return dff.to_dict("records")

The first line filters by the country input from the component id ‘slct_country’ and the second line filters by the manufacturer input, component id = ‘filter_dropdown’. Lastly, since you are returning the data for ‘datatable_id’, there only needs to be 1 output in the callback and we return 1 thing (dff.to_dict(“records”)) in the return statement. Hope this helps some!

Hi @kelly_gfc :slight_smile:

Thank you very much for your advice on callback and function!
It’s much better than I did first.

By the way for the output I would like to keep 2 output component that’s why I wrotte 2 dataTable in my code first.
I second time I would like to developpe my code to create different conditionnal dropdown. I am very motivated to enrich it and improve my skill with Dash.

But rightnow in my case, change callback didnt fix my problem and when I choose a value into my dropdown It didnt impact the output datatable “datatable_id”. With both input “slct_country” or “filter_dropdown”. I tried it on the second datatable too with id “table-containe” in case of Dash keep only the last dash_table step or something.

If you’re updating 2 data tables then your return statement would have 2 items to return separated with a comma… like:

return dff.to_dict("records"),  dff.to_dict("records")

Can you try printing your inputs to make sure they are coming in? Something like:

def display_table(country_input, manufacturer_input):
   print(country_input)
   df = df[df.Country == country_input]
   dff = df[df.Manufacturer_Code == manufacturer_input]

   return dff.to_dict("records")

That would be a good way to see that the inputs are actually coming in when the dropdown is clicked.

1 Like

Yes you got it, It seems input don’t work, when I select a value nothing about.
By the way, I’m working on a plateforme I can’t directly use a print or test my code by step that’s tought.
I have to launch it in one time so I can’t check only the function.

I have checked some code from @adamschroeder on his github he has very similar step but I can’t notice what is my problem.

app.layout = html.Div([

	
	html.H1("Global Referential of Network Products", style={'text-align': 'center',
															 'height':'60px',
															 'line-height': '60px',  
															 'border-bottom':'thin lightgrey solid',
															'font-size':'22px',
															'background-color' : '#ff7f00',
															'font-family': 'Helvetica 75','display': 'inline-block'
															,'border-radius' : '2px',
															'border' : '10px solid #ccc', 'color': 'black', 'border-spacing' : '10', 'border-collapse' :'separate'}),
	#SELECT COUNTRY 
	

	#DATASET INTERACTIF
	 html.Div([
		html.Label('Quick research input dataset'),
		dash_table.DataTable(
			id='datatable_id',
			data=df.to_dict('records'),
			row_deletable=True,
			columns=[
				{"name": i, "id": i, "deletable": False, "selectable": False} for i in df.columns
			],
			fixed_rows={ 'headers': True, 'data': 0 },
			#editable=False,
			filter_action="native",
			sort_action="native",
			sort_mode="multi",
			#row_selectable="multi",
			#selected_rows=[],
			page_action="native",
			page_current= 0,
			page_size= 10,
			style_cell={
			'whiteSpace': 'normal'
			},
			virtualization=False,
			style_cell_conditional=[
				{'if': {'Country': 'Country'},
				 'width': '30%', 'textAlign': 'left'},
				{'if': {'Manufacturer_Code': 'Manufacturer_Code'},
				 'width': '30%', 'textAlign': 'left'},
				{'if': {'Local_Code': 'Local_Code'},
				 'width': '30%', 'textAlign': 'left'},
				{'if': {'Item_Description': 'Item_Description'},
				 'width': '10%', 'textAlign': 'left'},
			],
			export_format='xlsx',
			export_headers='display',
			merge_duplicate_headers=False            
		),
		 dcc.Dropdown(id="slct_country",
				 options=[
					 {"label": country_list[0], "value": country_list[0]},
					 {"label": country_list[8], "value": country_list[8]},
					 {"label": country_list[7], "value": country_list[7]}],
				 multi=False,
				 value=country_list[0],
				 style={'width': "60%"}
				 ),
		 
	],className='row'),
	html.Div(
	children=[
		dcc.Dropdown(
			id="filter_dropdown",
			options=manucode_list,
			placeholder="-Select a Manu Code",
			multi=True
		)
		],className='row'),

	html.Div([
		dash_table.DataTable(
			id="table-container",
			columns=[{"name": i, "id": i} for i in df.columns.values],

			data=df.to_dict("records"),
			
			row_deletable=True,
			fixed_rows={ 'headers': True, 'data': 0 },
			style_cell_conditional=[
				{'if': {'Country': 'Country'},
				 'width': '30%', 'textAlign': 'left'},
				{'if': {'Manufacturer_Code': 'Manufacturer_Code'},
				 'width': '30%', 'textAlign': 'left'},
				{'if': {'Local_Code': 'Local_Code'},
				 'width': '30%', 'textAlign': 'left'},
				{'if': {'Item_Description': 'Item_Description'},
				 'width': '10%', 'textAlign': 'left'},
			],
			page_action="native",
			page_current= 0,
			page_size= 10,
			export_format='xlsx',
			export_headers='display',
			merge_duplicate_headers=False
			#virtualization = True
		 )

	],className='row'),
	
	
])

@app.callback(
	[Output(component_id='datatable_id', component_property='data'),
	 Output('table-container', 'data')
	],
	[Input(component_id='slct_country', component_property='value'),
	 Input('filter_dropdown', "value")
	])


def display_table(country_input, manufacturer_input):
	print(country_input)
	print(manufacturer_input)
	df = df[df.Country == country_input]
	dff = df[df.Manufacturer_Code == manufacturer_input]

	return dff.to_dict("records"),  dff.to_dict("records")

Are you getting any errors? I use PyCharm as my ide and can see print statements and errors when I run the app and initiate callbacks.

Nop I didnt get any errors. Maybe It’s because I didnt give you the first part of my code with my dataframe and list creation but it don’t change a lot.

I have the screen of my webapp with my 2 tables top and bottom on the interface and my 2 dropdown but there is no connection with elements.

This is your second dropdown:

dcc.Dropdown(
            id="filter_dropdown",
            options=manucode_list,
            placeholder="-Select a Manu Code",
            multi=True
        )

Because multi=True, manufacturer_input in the callback is a list, therefore you can’t filter it directly using a simple equality. Besides the value is not set by default, so you should expect the tables to be empty (manufecturer_input=[]) when the app starts (and the callback updates the first time).

This should work for you (in the callback):

dff = df[df.Manufacturer_Code.isin(manufacturer_input)]
1 Like

Thank you for your answer @jlfsjunior ! :slight_smile:

I dont get it, why multi=True in my dropdown impact bad the callback function ? It not should only complete the list of code with element available ? I select or act like a simple filter with one value if I took only one.

I called both variable empty in my function but it don’t change anything so I guess there is the point who block anything since beginning about my project. :smiling_face_with_tear:

def display_table(country_input, manufacturer_input):
print(country_input)
print(manufacturer_input)
manufecturer_input=
country_input=
df = df[df.Country == country_input]
dff = df[df.Manufacturer_Code.isin(manufacturer_input)]
#dff = df[df.Manufacturer_Code.isin(manufacturer_input)]
return dff.to_dict(“records”), dff.to_dict(“records”)

This is a pandas error… Whenever you use multi=True, the value prop will be a list and not a single value. If you try to compare a series (like df.Manufacturer_Code with a list using ==, you will get a ValueError. If multi=False, then you can use == for comparison.

In the code above, you are setting country_input=[] and trying to compare it with ==, which will give you a ValueError in the callback.

I strongly recommend you to run the application with debug=True to capture these errors.