Black Lives Matter. Please consider donating to Black Girls Code today.

Serialising/Deserialising Dash components

For the purposes of caching a layout across instances, I want to be able to serialise and deserialise my layout to save it in the database. This itself is relatively simple, as Component.to_plotly_json achieves this, and then I can just use json.dumps on the resulting dictionary (with some recursion to deal with serialising Children too).

However, when deserialising I’m using json.loads, and then feeding the dictionary to app.layout. For components in the dash_html_components namespace, this works fine and the components load. For components in other namespaces, however, I get a {namespace} was not found error when loading the dashboard. This includes components in dash_core_components. The namespaces are all imported in the script that handles layout loading, so I’m wondering if there’s anything in Dash itself that is causing this behaviour.

A little context: When Dash serves the page, it crawls the app.layout to see which component libraries are being used (e.g. dash_core_components). Then, with this list of unique component libraries, it serves the necessary JS and CSS bundles that are distributed with those component libraries.

So, what could be happenning here is that the initial layout doesn’t have any dash_core_components components? i.e. if we serving dash_core_components as the response of a callback then Dash might only see dash_html_components in the app.layout and so it doesn’t serve the necessary JS and CSS bundles that are required for the dash-core-components component that is rendered in the future.


Otherwise, I’m not sure what could be happening here. Dash isn’t able to properly crawl the layout in the way that it was regenerated? Dash uses the component’s .traverse() method to crawl the layout to extract each component’s _namespace: https://github.com/plotly/dash/blob/4e146bcfc41a522839a54d8dca359775d84ac807/dash/resources.py#L84-L85. traverse method here: https://github.com/plotly/dash/blob/4e146bcfc41a522839a54d8dca359775d84ac807/dash/development/base_component.py#L132

Cheers for the response, Chris. I managed to get around the issue with dash_core_components not being loaded as part of my deserialisation process; I use importlib to dynamically import the modules and instantiate the objects with their arguments. I then had a similar issue where it wasn’t serving the files for a custom component (dash_grid_layout), but the solution to that turned out to be that I was a moron and had forgotten to set the app to serve locally.

Thanks!

1 Like

So, what could be happenning here is that the initial layout doesn’t have any dash_core_components components? i.e. if we serving dash_core_components as the response of a callback then Dash might only see dash_html_components in the app.layout and so it doesn’t serve the necessary JS and CSS bundles that are required for the dash-core-components component that is rendered in the future.

@chriddyp I think that is exactly what’s happening in my case too. But I’m not sure if that should be considered a bug or I just don’t know how to solve it.

Sample App

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


app = dash.Dash()
app.layout = html.Div([
    html.H2('Demo'),
    # Works, if uncommented
    # dcc.Input(id='input1', type='text', value='Rendered ok'),
    html.Button('Show P', id='button1'),
    html.Button('Show Input', id='button2'),
    html.Div(id='container1'),
    html.Div(id='container2'),
])


@app.callback(
    Output('container1', 'children'),
    [Input('button1', 'n_clicks')],
)
def on_button1_click(clicks: int):
    if clicks:
        return html.P('That works!')


@app.callback(
    Output('container2', 'children'),
    [Input('button2', 'n_clicks')],
)
def on_button2_click(clicks: int):
    if clicks:
        return dcc.Input(id='input2', type='text')


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

Problem

This app has two buttons. “Show P”, it creates some text element, “Show Input” creates core Input() component.
image
Now the problem is with the Input component. After “Show Input” is pressed, I can see JavaScript error in console log:

Error: dash_core_components was not found.

If the line in here is uncommented:

app.layout = html.Div([
    html.H2('Demo'),
    # Works, if uncommented
    dcc.Input(id='input1', type='text', value='Rendered ok'),
    html.Button('Show P', id='button1'),
    html.Button('Show Input', id='button2'),
    html.Div(id='container1'),
    html.Div(id='container2'),
])

It works just fine. Well, because apparently dash core components library get’s loaded.

Questions

  • @lewis how did you solve your problem?
  • Is there an explicit way to tell Dash to load specific components? In my case Dash Core Components?
  • Is this a bug? Cause I have some spare time and motivation to work on this :slight_smile:

I corrected the problem by ensuring I had the necessary imports done at runtime, and loaded into my dash app. I guess the only correction you could do would be to inject the relevant JS files into the app when a component is loaded, even if it is after initial startup.

1 Like