Call python from JS

Hi,

PS I am a JS noob
I have got a website using django 4 and matplotlib
example https://bokeh.traimaocv.fr/index/frequence
In this page values in text box call numpy function and plot curve

Now I have got a bokeh page :
https://bokeh.traimaocv.fr/index/slider

In this page slider value call JS function and plot curve.
Is it possible to call numpy function ?

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.

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
        }));

@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.

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.

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)

And I have got a warning :
An attempt to set a forbidden header was blocked: referer bokeh-2.4.2.min.js line 424 > Function:20:12

What does it mean?

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).

Thanks. Everything is OK now. there is no django error.
Page is here my plot but it does not update curve. Working in progress…

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()))
1 Like