☰Read-only archive⌕
Looking for help with Bokeh?

This is a read-only archive. Ask questions and join the conversation on GitHub.

Visit Community Support on GitHub →
Archived discussions · Captured October 28, 2026

Call python from JS

Community Support
Bryan
Bryan

If you want to trigger real Python code from JS events (e.g. widgets, selections, etc) than that is specifically the reason the Bokeh server was created:

I won’t say it is impossible to call out to a REST API or something from a CustomJS but that is not typical usage, and you would have to set up the API endpoints and fetch or XMLHttpRequest calls yourself.

⌁   Link to post
LaurentBerger
LaurentBerger

thanks I will use this code below in my JS.
But I have got a problem with cookie. Always error 403
request seems good

XHRPOSThttps://bokeh.traimaocv.fr/index/slider
[HTTP/2 403 Forbidden 174ms]
-----------------------------2378569051379486391421896748
Content-Disposition: form-data; name="csrfmiddlewaretoken"
S2f5czHoE3hlT8aMGqFhfyJ8T7kpQIYPJPg4Ucb86L3QfkPToOwyKZ07gVsiciLz
Content-Disposition: form-data; name="freq"
2.6

my source code is

def sinus_slider(request: HttpRequest) -> HttpResponse:
    freq = 1
    b_ok, val = get_arg_post(request, ['freq'])
    if b_ok:
        freq = float(val[0])
        phase = float(val[1])

    x = np.linspace(0, 10, 500)
    y = np.sin(freq*x)

    source = ColumnDataSource(data=dict(x=x, y=y))

    plot = figure(y_range=(-10, 10), width=400, height=400)

    plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)

    amp_slider = Slider(start=0.1, end=10, value=freq, step=.1, title="Amplitude")

    callback = CustomJS(args=dict(source=source, amp=amp_slider),
                        code="""
        var inputElems = document.querySelectorAll('input');
        var csrfToken = '';
        var i=0;
        for (i = 0; i < inputElems.length; ++i) {
            if (inputElems[i].name === 'csrfmiddlewaretoken') {
                csrfToken = inputElems[i].value;
                break;
                }
            }
        const data = source.data;
        var xhr = new XMLHttpRequest();
        
        xhr.open("POST", "/index/slider", true);
        xhr.setRequestHeader('Content-Type', 'text/html; charset=utf-8');
        xhr.setRequestHeader('mode', 'same-origin');
        xhr.setRequestHeader("referer", "/index/slider");
        var dataForm = new FormData();
        dataForm.append('csrfmiddlewaretoken', csrfToken);
        dataForm.append('freq', amp.value);

        xhr.send(dataForm);
        """)

    amp_slider.js_on_change('value', callback)
    layout = row(
        plot,
        column(amp_slider),
    )

    script1, div  = components(layout, "my plot")
    html2 = file_html(layout, CDN, "my plot")
    #html2 = html2.replace("</head>","{% csrf_token %}</head>")
    code_html = render(request,"sinus_slider.html", dict(script1=script1, div=div))
    return code_html

<!doctype html>
<html lang="en">
  
  <head>
    
      <meta charset="utf-8">
      <title>my plot</title>
        <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.min.js"></script>
        <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"></script>
        <script type="text/javascript">
            Bokeh.set_log_level("info");
        </script>
        
      
      
    
  </head>
  
  {{ div | safe }}
  
  <body>
  <div>
{{ script1|safe }}
<form method="post">{% csrf_token %}</form>
</div>
<div>
</div>
 </body>
  
</html>
        var xhr = new XMLHttpRequest();
        
        xhr.open("POST", "/index/slider", true);
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.send(JSON.stringify({
        value: amp.value,
        csrfmiddlewaretoken: cookie
        }));
⌁   Link to post
Bryan
Bryan

@LaurentBerger There’s not really enough information here to go on, plus it’s not actually clear what interaction you are trying have happen. All I can really say is:

  • Your HTML is definitely malformed (duplicated doctype/head/body sections, at a minimum)
  • components definitely works when used correctly, there are lots of examples and tests that exercise it. If you are having issues with it, I’d suggest a dedicated new topic with a minimal code related only to that.
⌁   Link to post
LaurentBerger
LaurentBerger

I give my full code and now page is good
request is good but something is wrrong with cookie. ERROR 403

# Forbidden (403)

CSRF verification failed. Request aborted.

## Help

Reason given for failure:

CSRF token missing or incorrect.

In general, this can occur when there is a genuine Cross Site Request Forgery, or when [Django’s CSRF mechanism](https://docs.djangoproject.com/en/3.2/ref/csrf/) has not been used correctly. For POST forms, you need to ensure:

* Your browser is accepting cookies.
* The view function passes a `request` to the template’s [`render`](https://docs.djangoproject.com/en/dev/topics/templates/#django.template.backends.base.Template.render) method.
* In the template, there is a `{% csrf_token
    %}` template tag inside each POST form that targets an internal URL.
* If you are not using `CsrfViewMiddleware`, then you must use `csrf_protect` on any views that use the `csrf_token` template tag, as well as those that accept the POST data.
* The form has a valid CSRF token. After logging in in another browser tab or hitting the back button after a login, you may need to reload the page with the form, because the token is rotated after a login.

You’re seeing the help section of this page because you have `DEBUG =
  True` in your Django settings file. Change that to `False`, and only the initial error message will be displayed.

You can customize this page using the CSRF_FAILURE_VIEW setting.
⌁   Link to post
Bryan
Bryan

I am afraid I don’t have the expertise to help with the cookie issue, perhaps someone else here may be able to chime in (or a more general help forum like SO might be advised)

⌁   Link to post
Bryan
Bryan

I think that is the browser telling you that this:

xhr.setRequestHeader("referer", "/index/slider");

is not permitted. But again this is not an area of expertise for me, so I can’t really say more.

Edit: Yes, see the link below. You are not allowed to set a Referrer header. This has nothing to do with Bokeh, though (it is a restriction enforced on everyone by the browser).

⌁   Link to post
LaurentBerger
LaurentBerger

Problem solved using json request and this blog Updating a Bokeh Graph in a Django Website - DEV Community (and stackoverflow of course)

def get_arg_post(request, list_var):
    """
    Récupération des données POST dans une requète
    request --> objet HTTPrequest
    list-var --> liste des variables à extraire de la requête
    valeur retour --> booléen et liste des valeurs
    booléen False si les valeurs n'ont pas été trouvées
    """
    resultat = []
    if len(request.POST) > 0:
        for v in list_var:  
            if v in request.POST:
                try:   
                    resultat.append(request.POST[v])
                except:
                    return False, []
            else:
                return False, []
    else:
        return False, []
    return True, resultat

@csrf_protect
def sinus_slider(request: HttpRequest) -> HttpResponse:
    freq = 1
    b_ok, val = get_arg_post(request, ['freq'])
    if b_ok:
        freq = float(val[0])

    x = np.linspace(0, 10, 500)
    y = np.sin(freq*x)+freq
    print("Frequence slider= ",freq)
    source = ColumnDataSource(data=dict(x=x, y=y))

    plot = figure(y_range=(-10, 10), width=400, height=400,title="Ma Courbe",name="Mes_donnees")

    plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6,name="Mon_sinus")

    amp_slider = Slider(start=0.1, end=10, value=freq, step=.1, title="Amplitude")
    callback = CustomJS(args=dict(source=source, amp=amp_slider),
                        code="""
        var csrfToken = '';
        var i=0;
        var inputElems = document.querySelectorAll('input');
        var reponse='';
        for (i = 0; i < inputElems.length; ++i) {
            if (inputElems[i].name === 'csrfmiddlewaretoken') {
                csrfToken = inputElems[i].value;
                break;
                }
            }
        var xhr = new XMLHttpRequest();
        
        xhr.open("POST", "/index/slider_change", true);
        xhr.setRequestHeader('mode', 'same-origin');
        var dataForm = new FormData();
        dataForm.append('csrfmiddlewaretoken', csrfToken);
        dataForm.append('freq', amp.value);
        xhr.responseType = 'json';
        xhr.onload = function() {    
            reponse =  xhr.response
            source.data.x = reponse['x'];
            source.data.y = reponse['y'];
            const plot = Bokeh.documents[0].get_model_by_name('Mes_donnees')
            source.change.emit();
            }
        xhr.send(dataForm);
        """)

    amp_slider.js_on_change('value', callback)
    layout = row(plot, column(amp_slider))
    script1, div1  = components(layout, "Graphique")
    pos = div1.find('data-root-id="')
    id = int(div1[pos+14:pos+18])
    #script2, div2  = components(amp_slider, "slider freq")
    #html2 = file_html(layout, CDN, "my plot")
    #html2 = html2.replace("</head>","{% csrf_token %}</head>")
    code_html = render(request,"sinus_slider.html", dict(script1=script1, div=div1))
    return code_html

@csrf_protect
def sinus_slider_change(request: HttpRequest) -> HttpResponse:
    freq = 1
    b_ok, val = get_arg_post(request, ['freq'])
    if b_ok:
        freq = float(val[0])

    x = np.linspace(0, 10, 500)
    y = np.sin(freq*x)+freq
    source = base64.b64encode(x)
    
    return JsonResponse(dict(x=x.tolist(),y=y.tolist()))
⌁   Link to post

End of archived discussion · Replies are disabled