Multiple callback error

Trying to run a multiple callback to interact with 2 dropdown components in dash. Running the code below:

all_options = {
‘group_1’: series[1:5],
‘group_2’: series[6:10],
‘group_3’: series[11:-1],
}

app.layout = html.Div(children=[
html.P([html.Label(“Choose a group:”),
dcc.Dropdown(id=‘groups_dropdown’, options=[{‘label’: k, ‘value’: k} for k in all_options.keys()],
value=‘group_1’)],
style=dict(width=‘400px’)
),

html.P([html.Label(“Choose a series:”),
dcc.Dropdown(id=‘series_dropdown’)],
style=dict(width=‘400px’)
),
dcc.Graph(
id=‘plot_1_1’,
figure=fig,
style=dict(width=‘800px’)
)
])

@app.callback([Output(‘series_dropdown’, ‘options’)],
[Input(‘groups_dropdown’, ‘value’)])
def set_series_options(selected_group):
return [{‘label’: i, ‘value’: i} for i in all_options[selected_group]]

@app.callback([Output(‘series-dropdown’, ‘value’)],
[Input(‘series-dropdown’, ‘options’)])
def set_series_value(available_options):
return available_options[0][‘value’]

Causes this error:
AttributeError: ‘Div’ object has no attribute ‘keys’

Any help would be much appreciated, been struggling with this for a while

Oof, that’s not a very helpful error message! You’ve just changed the ID from series_dropdown to series-dropdown in the second callback. Thanks for bringing this up, we’ll fix that to give a more useful error.

Once you’ve fixed the IDs, you’ll notice that your Output's in the callback definition should not be wrapped in a list, unless you want to wrap the function return in a list as well (this is used for multiple outputs from one callback)

Thanks a lot! Sad to see that it was such a stupid thing. Also, the output is multiple there I just changed it a bit to simplify before posting here and forgot to remove the brackets. Cheers.

1 Like

Hello everyone,

I have the same error message but my id’s are the same and my output is not in a list

                                dcc.Dropdown(
                                    id='canopy_dropdown',
                                    options=[
                                        {'label': 'Sparse', 'value': 'sparse'},
                                        {'label': 'Dense', 'value': 'dense'},
                                        {'label': 'Very dense', 'value': 'vdense'},
                                        ],
                                    placeholder="Select a canopy",
                                     ),
                                html.Div(id='display_choice'),

@app.callback(Output(‘display_choice’, ‘children’),[Input(‘canopy_dropdown’, ‘value’)])
def set_display(selected_canopy):
return u’{}’.format(selected_canopy)

Thank you very much, I hope you could help me (I start being super desperate)

you can’t write callback for the same output more than once…

Hello - I’m also getting this error:
AttributeError: ‘Div’ object has no attribute 'keys’

After going through the responses here I haven’t found a solution. I’m trying to dynamically add tabs. When I have a single callback that adds the tabs, it works. The issue is when I try to add a second callback that gives content to the selected tab.

######################################
####### App layout ###################
######################################
app.layout = html.Div([
html.Div([dcc.Dropdown(id=“selected-tickers”, multi=True, value=[“PEP”],
options=[{“label”:x,“value”:x} for x in dfw.drop(columns=[‘SPY’])])],
className=“row”, style={“display”: “block”, “width”: “30%”, “margin-left”: “auto”,
“margin-right”: “auto”}),
html.Div(className=‘row’, children=[
html.Div(id=‘mytabs’, className=“six columns”, style={“width”: “28%”}),
html.Div(id=‘tabs-content’),
html.Div(dcc.Graph(id=‘graph-timeseries’), className=“six columns”, style={“width”: “68%”}) #style={“width”: “30%”})
]),
str(2003)},step=None),
html.H3(children=‘Model estimation’),
html.Div(id=‘regtable-div’)
], style={“width”: “80%”,“margin-left”: “auto”,“margin-right”: “auto”})

######################################
####### Tabs #########################
######################################
@app.callback(Output(‘mytabs’, ‘children’), [Input(‘selected-tickers’, ‘value’)])
def display_tabs(tickers):
alltabs =
for t in tickers:
alltabs.append(
dcc.Tab(label=t, value=t)
)
return dcc.Tabs(alltabs, value=tickers[0])

@app.callback(Output(‘tabs-content’, ‘children’),
[Input(‘tabs’, ‘value’)]) # id, key, title
def render_content(tab):
return html.Div([
html.H3(tab)
])

If I replace ‘mytabs’ with ‘tabs’, I get the error:

dash.exceptions.NonExistentPropException:
Attempting to assign a callback with
the property “value” but the component
“tabs” doesn’t have “value” as a property.

Thanks!

Hi nandan-nayak, thank you very much for your response, yet I am not sure to understand as I am a very beginner,

What should I do ?

Thank you!!

upload your code, so that we can help…

i worked with your code and didn’t get any error.
but you try by modifying the first callback by giving ‘id’
for t in tickers:
alltabs.append(
dcc.Tab(id=“tabs”,label=t,value=t)
)
return …

Hi @nandan-nayak - thanks for the reply. I tried adding the id like you suggested, but it didn’t work. Here’s a full example. I still get the error:

AttributeError: ‘Div’ object has no attribute 'keys’

(Also see the reference here from @valentijnnieman where he dynamically inserts tabs)

import dash
import numpy as np
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State

# Get data
tickers = ['PEP','GOOG','MSFT','AMZN','AAPL']
random_x = np.random.randn(100)
random_y = np.random.randn(100)

# Start application
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server

######################################
####### App layout ###################
######################################
app.layout = html.Div([
    html.Div([dcc.Dropdown(id="selected-tickers", multi=True, value=["PEP"],
                        options=[{"label":x,"value":x} for x in tickers])],
          className="row", style={"display": "block", "width": "30%", "margin-left": "auto",
                                  "margin-right": "auto"}),
    html.Div(className='row', children=[
        html.Div(id='mytabs',style={"width": "30%"}),
        html.Div(id='tabs-content'),
    ], style={"display":"flex","flex-wrap":"wrap"}),
], style={"width": "80%","margin-left": "auto","margin-right": "auto"})


######################################
####### Tabs #########################
######################################
@app.callback(Output('mytabs', 'children'), [Input('selected-tickers', 'value')])
def display_tabs(tickers):
    alltabs = []
    for t in tickers:
        alltabs.append(
            dcc.Tab(label=t)
        )
    return dcc.Tabs(alltabs,value=tickers[0])

@app.callback(Output('tabs-content', 'children'),[Input('tabs', 'value')])
def render_content(tab):
    return html.Div([
        html.H3(tab)
])

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

I figured out a solution. Rather than have the first callback return dcc.Tabs(), I have it return the values. Working below. I’d be interested if anyone knows how to get it working in the case where the callback returns dcc.Tabs? The problem with my solution before is that I can’t dynamically set the default value for dcc.Tabs.

import dash
import numpy as np
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State

# Get data
tickers = ['PEP','GOOG','MSFT','AMZN','AAPL']
random_x = np.random.randn(100)
random_y = np.random.randn(100)

# Start application
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server

######################################
####### App layout ###################
######################################
app.layout = html.Div([
    html.Div([dcc.Dropdown(id="selected-tickers", multi=True, value=["PEP"],
                        options=[{"label":x,"value":x} for x in tickers])],
          className="row", style={"display": "block", "width": "30%", "margin-left": "auto",
                                  "margin-right": "auto"}),
    html.Div(className='row', children=[
        dcc.Tabs(id='tabs'),
        html.Div(id='tabs-content'),
    ], style={"display":"flex","flex-wrap":"wrap"}),
], style={"width": "80%","margin-left": "auto","margin-right": "auto"})


######################################
####### Tabs #########################
######################################
@app.callback(Output('tabs', 'children'), [Input('selected-tickers', 'value')])
def display_tabs(tickers):
    alltabs = []
    for t in tickers:
        alltabs.append(
            dcc.Tab(label=t)
        )
    return alltabs

@app.callback(Output('tabs-content', 'children'),[Input('tabs', 'value')])
def render_content(tab):
    return html.Div([
        html.H3(tab)
])

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

This code works, you just send ID along with CHILDREN and VALUES in first callback
and also create a dcc.Tabs() with the id “tabs” which you are going to use it in second callback with empty children in layout.

import dash
import numpy as np
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State

# Get data
tickers = ['PEP','GOOG','MSFT','AMZN','AAPL']
random_x = np.random.randn(100)
random_y = np.random.randn(100)

# Start application
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server

######################################
####### App layout ###################
######################################
app.layout = html.Div([
    html.Div([dcc.Dropdown(id="selected-tickers", multi=True, value=["PEP"],
                        options=[{"label":x,"value":x} for x in tickers])],
          className="row", style={"display": "block", "width": "30%", "margin-left": "auto",
                                  "margin-right": "auto"}),
    html.Div(className='row', children=[
        html.Div(id='mytabs',style={"width": "30%"}),
        html.Div(id='tabs-content'),
      dcc.Tabs(id="tabs",value="tab-1",children=[]),
    ], style={"display":"flex","flex-wrap":"wrap"}),
], style={"width": "80%","margin-left": "auto","margin-right": "auto"})


######################################
####### Tabs #########################
######################################
@app.callback(Output('mytabs', 'children'), [Input('selected-tickers', 'value')])
def display_tabs(tickers):
    alltabs = []
    for t in tickers:
        alltabs.append(
            dcc.Tab(label=t,value=t)
        )
    return dcc.Tabs(id="tabs",children=alltabs,value=tickers[0])

@app.callback(Output('tabs-content', 'children'),[Input('tabs', 'value')])
def render_content(tab):
    return html.Div([
        html.H3(tab)
])

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

Thanks for this solution @nandan-nayak! This is nice, because you set the initial value in the callback with value=tickers[0].

It seems very strange that we need this line in the layout: dcc.Tabs(id="tabs",value="tab-1",children=[]),. This is somehow an invisible component?

There is a problem with this solution if the tabs-content relies on the tab value being valid. Eg if you have in the tab-content print(tickers[tab]), you get the error KeyError: “[‘tab-1’] not in index”. Although at least I can get fix this with if tab=='tab-1': return None, it’s just not elegant.

I’m going to add a range slider where the min and max of the range depend on the data. I guess I’ll need to do something similar, assigning dcc.RangeSlider in a callback and then also having an invisible component with dcc.RangeSlider(id=“daterange”,children=[]).

I didn’t work with the range sliders ,

One issue I’ve noticed is that the selected tab is reset to tickers[0] each time an entry is added to the dropdown.

Is there a way to preserve which tab is selected? (My actual tabs-content is time-intensive and I only want it to re-render when the user changes the tab)

Give “value=tickers[-1]” instead of “value=tickers[0]” in the return statement of first callback…

but you will get error if you remove all the element from the Dropdown

so solution is by modifying the return statement of the first callback

    if len(tickers)>0:
        return dcc.Tabs(id="tabs",children=alltabs,value=tickers[-1])
    else:
        return dcc.Tabs(id="tabs",children=alltabs,value="")

Thanks for the suggestion. This doesn’t solve it though - it resets the value to the last tab in the list each time a ticker is added. I was wanting to preserve whichever ticker was previously selected.

I’m currently looking at an alternative - using dcc Store to effectively cache my intensive results.

can you tell me the difference between using "global variables " and dcc.Store to store the values??

As far as I understand, global variables can’t be modified in Dash. It seems like originally you had to use a trick with a hidden Div if you wanted to store data from a callback. As of last year, dcc.Store allows you to do this.