TypeError: unhashable type: 'dict' in dash app with pattern matching callbacks

Hello. I have only been using Dash a few weeks and I’m now trying pattern matching callbacks. My app works completely fine as a standalone app (not sure if this is the correct terminology), but returns TypeError: unhashable type: ‘dict’ when integrating it into my Django project as a stateless Django app.

There appears to be 2 problems:

  1. Appears to be with the app.layout (it doesn’t seem to like me specifying the id for the dropdown as a dictionary)
  2. The pattern matching callbacks (e.g. ALL from dash.dependencies) does not work in DjangoDash (TypeError: list indices must be integers or slices, not str). But again works fine standalone in app = dash.Dash

BUT, everything works fine if I use

app = dash.Dash( __name__, external_stylesheets=["https://codepen.io/chriddyp/pen/bWLwgP.css"]
 )

But not with

app = DjangoDash('ABS_6291_0_55_003', external_stylesheets= ['https://codepen.io/childdyp/pen/bWLwgP.css'], suppress_callback_exceptions=True)

My app.layout::

external_stylesheets = ['https://codepen.io/childdyp/pen/bWLwgP.css']
app = DjangoDash('ABS_6291_0_55_003', external_stylesheets=external_stylesheets, suppress_callback_exceptions=True)

app.layout = html.Div(
    [
        html.H1("App title"),
        html.Div([
            html.A(
                id='excel-download',
                download="data.xlsx",
                children=[html.Button("Download Excel data", id="download-btn")],
                href="",
                target="_blank"
            )
        ], style={"width": "25%", "float": "right"}),
        html.Div([
            html.Div(children=
                [
                    html.P(["Select table:", dcc.Dropdown(id={'type': 'filter-dropdown',
                                                              'dimension': 'Select'},
                                                          options=[{'label': d, 'value': d} for d in select_table_options],
                                                          value=select_table_options[0],
                                                          clearable=False
                                                          )])
                ],
            ),
            html.Div(children=
                [
                    html.P(["Show all:", dcc.Dropdown(id={'type': 'filter-dropdown',
                                                          'dimension': 'Show'},
                                                      options=[{'label': 'None', 'value': 'None'}],
                                                      value=None, clearable=True
                                                      )])
                ],
            ),

            html.Div(id='dropdown-container-output'),
        ], style={"width": "25%", "float": "left"}),
        dcc.Graph(id="graph", style={"width": "75%", "display": "inline-block"})
    ]
)

I generate some extra drop down menus, which are dependent on one of the ‘Select’ dropdown specified above:

@app.callback(
    Output('dropdown-container-output', 'children'),
    [Input({'type': 'filter-dropdown', 'dimension': 'Select'}, 'value')]
)
def display_output(select):
    """ Show dropdown menus, only if they have data for the table selected """
    data_filtered = dataset.to_table(filter=ds.field('Table') == select, columns=dimensions).to_pandas()

    non_null_columns = [col for col in data_filtered.columns if data_filtered.loc[:, col].notna().any()]

    all_options = []
    for d in non_null_columns:
        all_options.append(get_options_without_all(data_filtered[d].unique()))

    all_start_values = []
    for d in non_null_columns:
        options = data_filtered[d].sort_values()
        if options.isnull().values.any():
            all_start_values.append('None')
        else:
            all_start_values.append(options[0])

    return html.Div(
        [
            html.P([d + ":", dcc.Dropdown(id={'type': 'filter-dropdown', 'dimension': d},
                                          options=all_options[idx],
                                          value=all_start_values[idx],
                                          clearable=False,
                                          )])
            for idx, d in enumerate(non_null_columns)
        ],
    )

Then make a chart and excel file that goes something like this (i’ll spare you the function details as they don’t seem relevant), but they are dependent on both the dropdowns generated in the callback above and the ones generated in the app.layout:

@app.callback(Output(“graph”, “figure”),
[Input({‘type’: ‘filter-dropdown’, ‘dimension’: ALL}, ‘value’),
Input({‘type’: ‘filter-dropdown’, ‘dimension’: ALL}, ‘id’)])
def make_figure(values, names):

@app.callback(Output(‘excel-download’, ‘href’),
[Input({‘type’: ‘filter-dropdown’, ‘dimension’: ALL}, ‘value’),
Input({‘type’: ‘filter-dropdown’, ‘dimension’: ALL}, ‘id’)])
def download_xl(values, names):

This is the output I get:

Internal Server Error: /django_plotly_dash/app/ABS_6291_0_55_003/_dash-layout
Traceback (most recent call last):
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/views.py", line 68, in layout
    response_data, mimetype = app.augment_initial_layout(resp, initial_arguments)
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 419, in augment_initial_layout
    reworked_data = self.walk_tree_and_replace(baseData, initial_arguments)
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 461, in walk_tree_and_replace
    r = self.walk_tree_and_replace(v, overrides)
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 461, in walk_tree_and_replace
    r = self.walk_tree_and_replace(v, overrides)
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 466, in walk_tree_and_replace
    return [self.walk_tree_and_replace(x, overrides) for x in data]
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 466, in <listcomp>
    return [self.walk_tree_and_replace(x, overrides) for x in data]
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 461, in walk_tree_and_replace
    r = self.walk_tree_and_replace(v, overrides)
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 461, in walk_tree_and_replace
    r = self.walk_tree_and_replace(v, overrides)
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 466, in walk_tree_and_replace
    return [self.walk_tree_and_replace(x, overrides) for x in data]
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 466, in <listcomp>
    return [self.walk_tree_and_replace(x, overrides) for x in data]
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 461, in walk_tree_and_replace
    r = self.walk_tree_and_replace(v, overrides)
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 461, in walk_tree_and_replace
    r = self.walk_tree_and_replace(v, overrides)
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 466, in walk_tree_and_replace
    return [self.walk_tree_and_replace(x, overrides) for x in data]
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 466, in <listcomp>
    return [self.walk_tree_and_replace(x, overrides) for x in data]
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 461, in walk_tree_and_replace
    r = self.walk_tree_and_replace(v, overrides)
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 461, in walk_tree_and_replace
    r = self.walk_tree_and_replace(v, overrides)
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 466, in walk_tree_and_replace
    return [self.walk_tree_and_replace(x, overrides) for x in data]
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 466, in <listcomp>
    return [self.walk_tree_and_replace(x, overrides) for x in data]
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 461, in walk_tree_and_replace
    r = self.walk_tree_and_replace(v, overrides)
  File "/home/marco/anaconda3/envs/dashboard_project/lib/python3.7/site-packages/django_plotly_dash/dash_wrapper.py", line 456, in walk_tree_and_replace
    replacements = self._replacements.get(thisID, {})
TypeError: unhashable type: 'dict'
HTTP GET /django_plotly_dash/app/ABS_6291_0_55_003/_dash-layout 500 [0.13, 127.0.0.1:51644]

@marco83 pattern matching callbacks are not supported by django-plotly-dash at the moment - there is a github ticket here. One consideration yet to be fully worked through is how to handle the storage of dynamic properties of the app.

Any help to move this forward (in the form of pull requests or commercial agreements or another form) most welcome!

As an alternative, could you structure your app to have some finite number N of dropdowns, and then dynamically enable and populate them? Slightly clunky, but would step around the need for pattern matching in the callbacks.

1 Like

Thanks delsim. Sounds like I will have to avoid using pattern matching callbacks for the time being. I also tried what you suggested - disabling drop downs. It works, but it’s a pity I can’t hide them from view as there are a lot of them!