How to call Python variable in JavaScript? - javascript

I have a Python function that returns some value. Also I connected to my project Google Charts. So I need to pass that value to a js function in html file of Google Charts. The project is on Django btw.
What is the most correct way to do this?
{% extends "gappi_tmp/wrapper.html" %}
{% block content %}
<head>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
['Task', 'Hours per Day'],
['lol',11], // The variable should be here, instead of '11'
['Eat', 11] // Here another variable
]);
var options = {
title: 'Inbox | Outbox'
};
var chart = new google.visualization.PieChart(document.getElementById('piechart'));
chart.draw(data, options);
}
</script>
</head>
<body>
<div style="padding-top: 5px; background: cornflowerblue; width: auto; height: 300px;" id="piechart"></div>
</body>
{% endblock %}

You should render your template with a context:
https://docs.djangoproject.com/en/2.0/ref/templates/api/#rendering-a-context
The method of passing the context to the template depends on how your views are written.
Function-based views
Pass the context dictionary to the render() function:
https://docs.djangoproject.com/en/2.0/topics/http/shortcuts/#optional-arguments
from django.shortcuts import render
def my_view(request):
# View code here...
context = {'foo': 'bar'}
return render(request, 'myapp/index.html', context=context)
Class-based views
Write your own implementation of the add_context_data() method: https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-display/#adding-extra-context
from django.views.generic import DetailView
from books.models import Book, Publisher
class PublisherDetail(DetailView):
model = Publisher
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
# Add in a QuerySet of all the books
context['book_list'] = Book.objects.all()
return context
Once you passed key: value context to the template, you should use it in the template like this: {{ key }}.
https://docs.djangoproject.com/en/2.0/topics/templates/#variables
<script type="text/javascript">
var a = "{{ key|escapejs }}";
</script>
escapejs template filter is required to prevent possible XSS vulnerabilities. If you need to pass JSON, you could check out Django ticket #17419

In case you want to access python vars in a separated js file. You can define js global vars with the value of python vars, then access those global vars in a separated js file.

Generate the above page dynamically in Django including the necessary json data object from your python code
from here

In case that you have you js file inside your html file, and not in a seperated js file, since variables passed through context are available in the rendering templates, you can proceed that way.
After sending all the variables via context to template.
var jsVariable = '{{django_value}}';
var data = google.visualization.arrayToDataTable([
['Task', '{{ task_variable }}'], // as string
['lol',{{lol_variable}}], // as integer
['Eat', {{eat_variable}} ]
]);
You can also loop through.
var data = google.visualization.arrayToDataTable([
{% for item in queryset %} // {% for item in json_response %}
['Task', '{{ task.attribute }}'],
{% endfor %} // {% endfor %}
]);
Basically, you must know that you can use it, it depends of what you really want to do.

Related

Pass Django list as argument to Javascript

I have a list passed through the context into the html page of a Django project which I want to read inside of a .js which contains chartjs code. The problem is that .js is reading as string and not as a list/array.
views.py
def index(request):
context = {
"data": [1, 2, 3, 4, 5]}
return render(request, 'index.html', context)
Then, in the html page I have to pass the {{ data|safe }} to a javascript like so:
<script src='{% static "js/chart.js/linechart.js" %}'
var label = "fakelabel";
var index = {{ data|safe }};
></script>
Inside the .js I'm reading the 'index' input as:
document.currentScript.getAttribute("index")
How can I fix this? ('label' is read correctly as str indeed).
{{ data|safe }} is not a safe way to output a Python object as JavaScript.
Use json_script if you want to be safe
This is how to do it.
Write your object as json:
data = {'numbers':['1', '2', '3', '4', '5']}
def index(request):
context = {"data": data}
return render(request, 'index.html', context)
<script>
var index = {{ data|json_script:'mydata' }};
<script>
Then you can use the index variable into another scrip like this:
<script>
const data = JSON.parse(document.getElementById('mydata').textContent);
mydata = data['numbers'];
</script>
You could (and probably should) point to an external JavaScript file rather than using embedded JavaScript.
read here for more details
You can use ajax to fetch list or dict data from views and you need to add jQuery cdn in html file.
from django.http import JsonResponse
def indexAjax(request):
context={"data":[1,2,3,4,5]}
return JsonResponse(context,safe=False)
Inside your js file or script tag
function fun(){
$.ajax({
url:"{% url'indexAjax" %},
dataType:'json',
type:'GET',
success:function(res){
console.log(res.data)
//here you can write your chartjs
}
})}
The correct way to fix the above problem is:
views.py
def index(request):
context ={"data": [1, 2, 3, 4, 5]}
return render(request, 'index.html', context)
charts.html
Then, in the html script you should add {{ data|json_script:"index" }} like you you do with the 'static' tag. Then, always in your .html script you do not need to pass the 'index' list. Therefore when you call the javascript inside your .html you just need to pass:
{{ data|json_script:"index" }}
<script src='{% static "js/chart.js/linechart.js" %}'
var label = "fakelabel";
></script>
js/chart.js/linechart.js
Finally, inside you separate javascript (in this case "js/chart.js/linechart.js"), we can fetch the list with:
JSON.parse(document.getElementById('index').textContent)

How do I set the variables in this javascript file to match variables from Flask/Python?

I have a Javascript file that enables drop down menus dynamic (i.e. a selection from one drop down impacts the others). I would like to use this file in conjunction with multiple forms but I have hard-coded the starting variables in this file to html elements 'inverter_oem' and 'inverter_model_name'.
In other forms, I will need to reference different objects.
How can I accomplish this?
I would like to create the variables in Javascript this way:
const selecting_dropdown_menu = document.getElementById(variable1_from_flask);
const filtered_dropdown_menu = document.getElementById(variable2_from_flask);
My current approach is to try and pass variables from the Flask route using render_template. (I have also tried other stack overflow solutions but no luck)
#db_queries.route('/db_query_dynamic_form/<form_name>', methods=['GET', 'POST'])
def db_query_dynamic_form(form_name):
# use methods to get form and endpoint for outside loop
if request.method == 'POST':
# inside loop when form complete
return render_template('list.html', result=query_results["filtered_tables"], columns=query_results["column_names"])
# added variable to pass to jinja template
return render_template(form_endpoint["endpoint"], form=form_endpoint["form"], var="value1")
The jinja template form is as follows
% extends "base.html" %}
{% block title %}Inverter Dynamic Search{% endblock %}
{% block heading %}Inverter Dynamic Search{% endblock %}
{% block content %}
<form method="POST">
{{ form.hidden_tag()}}
{{ form.inverter_oem.label }}{{ form.inverter_oem }}<br>
{{ form.inverter_model_name.label }}{{ form.inverter_model_name }}<br>
{{form.submit()}}
{{test}}
</form>
<script>
myVar = {{ var }}
</script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="{{ url_for('static', filename='search_inverter_dynamic.js') }}"></script>
{% endblock %}
However, I dont get a value in the javascript file when I try
console.log(myVar);
Here is the original JScode with the "hardcoded" variables
//acquire inverter_oem value from form
const inverter_oem_select = document.getElementById('inverter_oem');
const inverter_model_name_select = document.getElementById('inverter_model_name');
//add event listener to capture selected oem in dropdown
inverter_oem_select.addEventListener('click', (e) => {
let oem = '../modelNameByOEM//' + e.target.value;
console.log(oem);
getIt(oem);
});
//recover a JSON containing the list of models from a specific oem
async function getIt(input) {
try {
const res = await axios.get(input);
console.log(res.data);
optionHTML = '';
for (let modelName of res.data.modelNameOem) {
//console.log(res.modelName.name);
optionHTML += '<option value="' + modelName.name + '">' + modelName.name + '</option>';
//console.log(optionHTML);
}
inverter_model_name_select.innerHTML = optionHTML;
} catch (err) {
console.log('Could not reach endpoint modelNameByOEM')
console.log(err);
};
}
Jinja does not know that you want to pass a variable inside a JavaScript environment, so it cannot add the quotation marks automatically around a string variable. It just replaces {{ var }} with value1 causing a syntax error. Just add the quotation marks and then you can access myVar inside the JS environment:
<script>
myVar = "{{ var }}"
</script>
Note: if you want to pass a more complex data structure and/or user submitted values, you should look at the tojson filter that safely renders the data.

Refresh a div in Django using JQuery and AJAX - Django ForLoop Issues

I have a django project that I'm trying to add a custom "online" state to (boolean field in a model). I want to refresh a div periodically to show if a user is now online. The div which is refreshed, with the class button-refresh, is in an included HTML file.
The issue is that the include doesn't work with my for loop in the original HTML file, none of the "professionals" data is retrieved from the server. I'm pretty new to django, my assumption is that the refresh_professionals.html file is retrieved with the ajax request and is entirely separate from the all_professionals.html and then included, without ever being a part of the for loop meaning that the {{ professional.professional_profile.online }} syntax doesn't work.
Any ideas on how to fix this issue? If there is a better way to do this, let me know. Thanks.
all_professionals.html
{% for professional in professionals %}
...
{{ professional.name }}
{% include 'professionals/refresh_professionals.html' %}
...
{% endfor %}
...
{% block postloadjs %}
{{ block.super }}
<script>var global_url = "{% url 'refresh_professionals' %}";</script>
<script src="{% static 'professionals/js/professionals.js' %}"></script>
{% endblock %}
refresh_professionals.html
<div class="col button-refresh">
{% if professional.professional_profile.online is True %}
<p class="custom-button mb-1 w-25 mx-auto">Chat</p>
{% else %}
<p class="custom-button-disabled mb-1 w-25 mx-auto">Unavailable</p>
{% endif %}
<p>{{ professional.price_chat }}/min</p>
</div>
professionals.js
$(document).ready(function(){
setInterval(function() {
$.ajax({
url: global_url,
type: 'GET',
success: function(data) {
$('.button-refresh').html(data);
}
});
}, 5000)
});
urls.py
urlpatterns = [
path('', views.view_all_professionals, name='view_all_professionals'),
path('refresh/', views.refresh_professionals, name='refresh_professionals'),
]
views.py
def view_all_professionals(request):
"""A view to return the professionals page"""
professionals = Professional.objects.all()
languages = Languages.objects.all()
context = {
'professionals': professionals,
'languages': languages,
}
return render(request, 'professionals/all_professionals.html', context)
def refresh_professionals(request):
"""A view to refresh the online button section"""
professionals = Professional.objects.all()
context = {
'professionals': professionals,
}
return render(request, 'professionals/refresh_professionals.html', context)
EDIT
I've followed Daniel's advice and am now returning a JSON object. This is the updated code
professionals.js
$(document).ready(function(){
setInterval(function() {
$.ajax({
url: global_url,
type: 'GET',
success: update_professionals,
});
}, 5000)
});
function update_professionals(response){
// unpack the response (context) from our view function:
var professionals = response.professionals;
// update html:
var i;
for (i = 0; i < professionals.length; i++) {
$('#professional-name' + i).text('professionals.name' + i);
};
};
views.py
def refresh_professionals(request):
"""A view to refresh the professionals section page"""
professionals = Professional.objects.all()
professionals = serializers.serialize("json", professionals)
context = json.dumps({
'professionals': professionals,
})
return HttpResponse(context)
The issue I'm facing now is referencing the professionals data. It's returning uncaught errors. The forloop is necessary because in my HTML I've got a series of IDs with a number attached to the end using a django forloop.counter. Any advice would be appreciated. Thanks.
Okay, lets assume we have a view function like so:
from django.http import HttpResponse
import json
def refresh_professionals(request):
"""A view to refresh the online button section"""
professionals = Professional.objects.all()
# convert to json friendly format:
professionals = ...
context = json.dumps({
'professionals': professionals,
})
return HttpResponse(context)
This assumes professionals now looks something like (the actual structure will obviously be different):
professionals = {"id":1, "name":"Jason"}
Now, in our js file we should have an ajax request like so (wrapped in a setInterval method, etc.):
$.ajax({
url: global_url,
type: 'GET',
success: update_professionals, // reference to an ajax-success function
});
And our success function like so:
update_professionals(response) {
// unpack the response (context) from our view function:
// use JSON.parse() to convert a string to json
var professionals = JSON.parse(response.professionals);
// update html:
$('#professional-name').text(professionals.name)
}
This assumes we have an HTML element like so:
<div id="professionals-name"> Sue </div>
The difference is we are using js to update the HTML on the fly instead of trying to re-render an HTML template likely will require a page refresh.

Django, syntax for insering Javascript to template

I would like to embed Bokeh plot in Django template using Json output.
http://docs.bokeh.org/en/latest/docs/user_guide/embed.html#json-items
Json output is ready for query in database. Plot should be rendered to div with specific ID.
Documentation says to use Json output in template with following code function:
item = JSON.parse(item_text);
Bokeh.embed.embed_item(item);
Please advise the correct syntax for use in template:
<div id="title"></div>
<script>
function(response) { return item = JSON.parse( {{plot_json}} ); }
function(item) { Bokeh.embed.embed_item(item); }
</script>
View file:
def home(request):
plot_json = Price_Charts.objects.using('llweb').values('timeframe_1h').filter(symbol_pair='ETH')
context = {
'plot_json': plot_json
}
return render(request, "home.html", context)
I don't know much about Bokeh, but I know that you need to ensure that the JSON object is read correctly in the Django template as JavaScript and not auto-escaped. Give autoescape off a try along with the Bokeh 'then' syntax.
<div id="title"></div>
<script>
fetch('/plot')
.then(function(response) {
{% autoescape off %}
return item = JSON.parse( {{plot_json}} );
{% autoescape on %}
})
.then(function(item) { Bokeh.embed.embed_item(item); })
</script>
Maybe this simplified jinja2 example can help you (tested on Bokeh v1.0.4). Run it as:
python myapp.py
The file and directory structure:
myapp
|
+---myapp.py
+---templates
+---index.html
myapp.py
import io
import json
import jinja2
import numpy as np
from bokeh.plotting import figure, curdoc
from bokeh.embed import json_item
from bokeh.resources import CDN
plot = figure()
plot.line(np.arange(10), np.random.random(10))
curdoc().add_root(plot)
renderer = jinja2.Environment(loader = jinja2.FileSystemLoader(['templates']), trim_blocks = True)
html = renderer.get_template('index.html').render(resources = CDN.render(), item_json_object = json.dumps(json_item(plot)))
filename = 'json_items.html'
with io.open(filename, mode = 'w', encoding = 'utf-8') as f:
f.write(html)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
{{ resources }}
</head>
<body>
<div id="myplot"></div>
<script>
Bokeh.embed.embed_item({{ item_json_object }}, "myplot");
</script>
</body>
</html>
It seems that the result of json.dumps(json_item(plot)) passed to the template is already a JSON object so you cannot use JSON.parse() on it. Or make sure you really pass a string object to this function.
The Bokeh documentation you refer to points to an example which is different from this one in the sense the plots data is loaded dynamically using JS fetch() method at page loading time in browser while here the plots data is attached to the page at template rendering time.

How can I pass a jinja2 object to a jquery function as parameter?

In my python script, I have something like this
students = getStudent()
return render_template('index.html', students = students)
students is a list of student, and each student has attributes like name, id, gpa, ....
In index.html, I have a selection like this
<select class="form-control" onchange="changeStudent">
{% for student in students %}
<option>{{student.name}}</option>
{% endfor %}
</select>
Now I want to implement a jquery function that can do can for example print out the student information if that student is selected.
function changeStudent(student) {
alert(student.name)
}
How can I do that? Thanks in advance.
If you're using an inline <script type="application/javascript"> element in your HTML you can just drop it straight in there.
Just make sure the object in your context is a JSON formatted string and that the data is genuinely safe.
view.py
import json
def index_view():
students = json.dumps(getStudent())
return render_template('index.html', students = students)
index.html
<html>
<body>
<script type="application/javascript">
var students = {{ students|safe }};
// do your thang...
</script>
</body>
</html>

Categories