Error: Model ‘MultiChoice’ does not exist

Hello Community, @Bryan . After I upgraded from 2.2.3 to 2.4.3 I am experiencing problems to render the graphs in the browser. Messages in the browser (Firefox, Chrome, Safari) are:

Error: Model 'MultiChoice' does not exist. This could be due to a widget or a custom model not being registered before first usage.
    get https://cdn.bokeh.org/bokeh/release/bokeh-2.4.3.min.js:166
    _instantiate_object https://cdn.bokeh.org/bokeh/release/bokeh-2.4.3.min.js:165
    _instantiate_references_json https://cdn.bokeh.org/bokeh/release/bokeh-2.4.3.min.js:165
    from_json https://cdn.bokeh.org/bokeh/release/bokeh-2.4.3.min.js:165
    w https://cdn.bokeh.org/bokeh/release/bokeh-2.4.3.min.js:163
    embed_items https://cdn.bokeh.org/bokeh/release/bokeh-2.4.3.min.js:163
bokeh-2.4.3.min.js:166:1015

The same happens with “div”

The code worked fine when it was running in Jupiter. Once I moved to a virtual environment in python, the error appears.

Any idea how to solve the problem? Do you need more information? Thank you. Rodrigo

I can only speculate given this limited information, but the most likely explanation is that you are not loading the (separate) widgets bundle from CDN. BokehJS is split into separate bundles so that users who don’t need various features (e.g. widgets, tables, math-text) don’t have to pay the cost of heavier page loads. In the notebook, all bundles are always automatically loaded (because there is no way to know ahead of time what models might be used), but if you are embedding in a way that uses manual page templates, then including all the relevant bundles is up to you, e.g. for widgets something like:

<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-x.y.z.min.js"
        crossorigin="anonymous"></script>

All the various bundles are listed at the bottom of this section:

https://docs.bokeh.org/en/latest/docs/user_guide/output/embed.html#components

cc @Timo do we have any dedicated section that lists all the bundles?

They are listed in multiple places:

Additionally, the reference guide for widgets includes a note about adding bokeh-widgets.js (widgets — Bokeh 3.0.3 Documentation)

1 Like

Thank you @Bryan @Timo I will try to expand the information as well as to share with the community what I have found on this topic.

  1. My final purpose have been to build a multi line graph with interactive widgets capable to include or remove lines. Additionally my intention was to include a hover that show x and y data as well as the selected categories. On this respect I consulted several examples on this community. I started with Bokeh 2.2.3 and found that hover does not work, so I tried 2.4.3 and it worked. A finial short example is posted here, because I think could be useful for people with the same purpose.
import pandas as pd
from bokeh.layouts import row
from bokeh.models import MultiChoice, ColumnDataSource, CustomJS, CDSView, HoverTool, CustomJSFilter, MultiSelect
from bokeh.plotting import figure, show
from bokeh.palettes import Category20

df_ini = pd.DataFrame(data={'ILLUMINANT': {0: '01_CFL', 1: '01_CFL', 2: '03_D65', 3: '03_D65', 4: '01_CFL', 5: '01_CFL', 6: '03_D65', 7: '03_D65', 8: '01_CFL', 9: '01_CFL', 10: '03_D65', 11: '03_D65', 12: '01_CFL', 13: '01_CFL', 14: '03_D65', 15: '03_D65', 16: '01_CFL', 17: '01_CFL', 18: '03_D65', 19: '03_D65'},
 'MONITOR': {0: '01_WPC', 1: '04_DWPC', 2: '01_WPC', 3: '04_DWPC', 4: '01_WPC', 5: '04_DWPC', 6: '01_WPC', 7: '04_DWPC', 8: '01_WPC', 9: '04_DWPC', 10: '01_WPC', 11: '04_DWPC', 12: '01_WPC', 13: '04_DWPC', 14: '01_WPC', 15: '04_DWPC', 16: '01_WPC', 17: '04_DWPC', 18: '01_WPC', 19: '04_DWPC'},
 'WAVELENGTH': {0: 400, 1: 400, 2: 400, 3: 400, 4: 410, 5: 410, 6: 410, 7: 410, 8: 420, 9: 420, 10: 420, 11: 420, 12: 430, 13: 430, 14: 430, 15: 430, 16: 440, 17: 440, 18: 440, 19: 440},
 'SRF': {0: 11.4083333, 1: 18.268, 2: 10.087, 3: 17.2156667, 4: 16.6256667, 5: 25.0633333, 6: 20.1476667, 7: 27.872, 8: 36.044000000000004, 9: 39.304, 10: 48.1666667, 11: 48.97, 12: 55.265666700000004, 13: 49.8693333, 14: 79.611, 15: 67.3716667, 16: 59.5793333, 17: 51.5753333, 18: 86.417, 19: 69.7326667}})

df_ini.reset_index(inplace=True)

df_ini['ALL_CATERGORIES'] = '-0-' + df_ini['ILLUMINANT'] + '-1-' + df_ini['MONITOR']

ALL_CATEGORIES_group = df_ini.ALL_CATERGORIES.unique()

df = pd.DataFrame()
df['all_categories'] = ALL_CATEGORIES_group
df['xs'] = [df_ini.loc[df_ini.ALL_CATERGORIES == i].WAVELENGTH for i in ALL_CATEGORIES_group]
df['ys'] = [df_ini.loc[df_ini.ALL_CATERGORIES == i].SRF for i in ALL_CATEGORIES_group]
df['color'] = (Category20[len(ALL_CATEGORIES_group)])[0:len(ALL_CATEGORIES_group)]
df['illuminant'] = df['all_categories'].str.extract('-0-(\w+)')
df['monitor'] = df['all_categories'].str.extract('-1-(\w+)')

df = df.astype('object')

source_01 = ColumnDataSource(data=df)

ILLUMINANTS_initial = 1
MONITOR_initial = 1
 
ILLUMINANTS_group = df['illuminant'].unique()
MONITOR_group = df.monitor.unique().tolist()

ILLUMINANT_select = MultiChoice(value=list(ILLUMINANTS_group[:ILLUMINANTS_initial]), options=list(ILLUMINANTS_group), title="Illuminants", width=200, placeholder='Please select one item')

MONITOR_select = MultiChoice(value=list(MONITOR_group[:MONITOR_initial]), options=list(MONITOR_group), title="Monitors")

illumi_filter = CustomJSFilter(args=dict(illumi_choice=ILLUMINANT_select), code="""
    const indices = []
    for (var i = 0; i < source.get_length(); i++) {
        if (illumi_choice.value.includes(source.data['illuminant'][i], 0)) {
            indices.push(i)
        }
    }
    return indices
""")

monit_filter = CustomJSFilter(args=dict(monit_choice=MONITOR_select), code="""
    const indices = []
    for (var i = 0; i < source.get_length(); i++) {
        indices.push(monit_choice.value.includes(source.data['monitor'][i], 0))
    }
    return indices
""")

view = CDSView(source=source_01,filters=[illumi_filter, monit_filter])

ILLUMINANT_update = CustomJS(args=dict(cb=ILLUMINANT_select,source=source_01,filt=illumi_filter)
    ,code='''
    var active_lbls = cb.value
    var new_filt = []
    for (var i=0;i<source.data['illuminant'].length;i++){
            if (active_lbls.includes(source.data['illuminant'][i])){
                    new_filt.push(i)
                    }                        
            }
    filt.indices = new_filt
    source.change.emit()
    '''
    )

MONITOR_update = CustomJS(args=dict(cb=MONITOR_select,source=source_01,filt=monit_filter)
    ,code='''
    var active_lbls = cb.value
    var new_filt = []
    for (var i=0;i<source.data['monitor'].length;i++){
            if (active_lbls.includes(source.data['monitor'][i])){
                    new_filt.push(i)
                    }                        
            }
    filt.indices = new_filt
    source.change.emit()
    '''
    )

ILLUMINANT_select.js_on_change('value', ILLUMINANT_update)
MONITOR_select.js_on_change('value', MONITOR_update)

p = figure(x_range=(395, 445), y_range=(0, 170))

hover_lines = HoverTool(
        show_arrow=False,
        line_policy='next',
    tooltips=[
        ('Illuminant', '@illuminant'),
        ('Monitor', '@monitor'),
        ('SRF', '$y'),
        ('Wavelength', '$x')
    ]
)
p.add_tools(hover_lines)

p.multi_line("xs", "ys", line_width=2, source=source_01, view=view, line_color='color')

show(row([p, ILLUMINANT_select, MONITOR_select]))
  1. When I implemented a similar code as part of a Phyton-Flask web app, problems appeared. The initial post of this series recorded one of them. So, I tried several options and finally, in my case all problems gone with Bokeh 2.3.3, with that version hover works fine and there is no conflicts with other components and libraries in the web app.

Thank you. Regards, Rodrgo.

I have to admit I don’t know what to make of this (what is “conflict” here? Is this just what was reported in the OP? Or something else?)

Just to be clear, in order to use widgets like MultiChoice, the separate bokeh-widgets bundle has to be included somehow. That’s been true all the way back to 0.x versions and is just as true for 2.3.3 as it is for 2.4.3. So I don’t know why that would make any difference, unless your Python/JS versions were mismatched. The BokehJS versions included in the page template must match the python Bokeh version that generated the content to embed.

It’s not really possible to comment further, though, without a complete Minimal Reproducible Example (of a flask app, since that is where the actual problems were).

Thank you. Yes, it is just I reported in the OP. That message appears in the console browser and no graph was displayed, so the web app did not work as expected. All the problems disappear when I moved from 2.4.3 to 2.3.3
The web app is build with python 3.7 and Flask 1.1.1 and it is deployed using the Pythonanywhere (now part of Anaconda) services. It will very difficult for me to build a Minimal Reproducible Example on that system.
Thankfully, the web app is now working fine with all Bokeh capabilities to show interactively multi lines graphs with hover. Thank you again for the community support.
Regards, Rodrigo

Just so there is no misunderstanding for anyone else who finds this topic, the code above works perfectly well with 2.4.3:

The issue is almost certainly a usage problem with the page template, but if that cannot be shared, then there is not much else we can say.

Thank you @Bryan I can share with you the following:

The final part of the previous code in the web app:

script01_f, div01_f = components(graph_final)
    cdn_js_f=CDN.js_files[0]
    cdn_wd_f=CDN.js_files[1]
    ##
    return render_template("embed.html", script01_html=script01_f, div01_html=div01_f, cdn_js_html=cdn_js_f, cdn_wd_html=cdn_wd_f)

And the template (embeded.html) is:

<!doctype html>

<html lang="en">
<!-------->  
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=yes">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">  
    <title>LAUNDRY-VR Graph</title>
    <!-- <link rel="stylesheet" href="{{ cdn_css_html|safe }}" type="text/css" /> -->
    <script type="text/javascript" src="{{ cdn_js_html|safe }}" crossorigin=”anonymous” ></script>
    <script type="text/javascript" src="{{ cdn_wd_html|safe }}" crossorigin=”anonymous” ></script>
  </head>
  <!-------->
  <!-------->
  <body>
    {{ script01_html|safe }}
    {{ div01_html|safe }}
  </body>
<!-------->  
</html>

Regards. Rodrigo

This is probably the problem. It’s not safe to make assumptions like this, you always need to put all of the values in js_files in the page. IIRC there are additional bundles in 2.4.3 (compared to 2.3.3), so there are probably more than 2 elements in js_files, in which case you are now throwing some away.

Edit: E.g. Jinja supports loops in templates, so you can just pass js_files to the template, and let it do the work to loop over all the values to insert them.

Thank you @Bryan. Is it also an option to precisely declare the version? Like:

<script src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.3.min.js"
        crossorigin="anonymous"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.3.min.js"
        crossorigin="anonymous"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.3.min.js"
        crossorigin="anonymous"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.4.3.min.js"
        crossorigin="anonymous"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-2.4.3.min.js"
        crossorigin="anonymous"></script>

If the code needs all of that?
Regards, Rodrigo

Certainly, but you will have to remember update your template manually in case you ever change Python Bokeh version. Generally speaking, the Python and JS versions need to match exactly.

Thank you @Bryan I have tried and all is working fine. So, the initial post has been absolutely solved. Thank again. Regards, Rodrigo