Django, syntax for insering Javascript to template - javascript

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.

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)

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.

How to call Python variable in 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.

Load json data for the first time request and to display the same in Home Page

I am using Vue.js for the first time. I need to serialize the objects of django
views.py
def articles(request):
model = News.objects.all() # getting News objects list
modelSerialize = serializers.serialize('json', News.objects.all())
random_generator = random.randint(1,News.objects.count())
context = {'models':modelSerialize,
'title' : 'Articles' ,
'num_of_objects' : News.objects.count() ,
'random_order' : random.randint(1,random_generator) ,
'random_object' : News.objects.get(id = random_generator ) ,
'first4rec' : model[0:4],
'next4rec' : model[4:],
}
return render(request, 'articles.html',context)
I have tried to display serialized json data in html its working fine there,
Now , how to intialize json data in vue instance and to access in html using v-repeat attribute.
https://jsfiddle.net/kn9181/1yy84912/
Please can any one help???
A simple example.
views.py
def articles(request):
context {
'articles' : ['a1','a2','a3']
}
return render(request, 'articles.html', context)
articles.html
{% verbatim %}
<div id="app">
<ul>
<li v-for="a in articles">{{ a }}</li>
</ul>
</div>
{% endverbatim %}
<script>
new Vue({
el : "#app",
data : function(){
return {
articles : {{ articles | safe }}
}
}
})
</script>
Things to watch out for :
The verbatim tag to stop Django from rendering the contents of this block tag since Vue uses the same interpolating symbols.
The safe filter to prevent Django from escaping the contents.
If you are passing a dictionary, consider turning it into JSON first
Generally speaking, prefer passing data to Vue via Ajax

Web text input save into database without form- Django Javascript

I have an sqlite3 database setup with django 1.6 where I want web users to be able to enter text and see it in 3 live tables (high, low and latest) all on the same page. The one page ideally should have text entry, voting, display of the three tables updated when new entries go in or are voted on, and a search if possible if all that gets sorted (personal project, not commercial). I am also on win7 64 if that matters...
Currently I have: a working database the three tables displaying in a web page without update and some web text input via js (and failing to save to the database in Django).
I shied away from forms at first, as they seem to want separate html pages for input. Asking an experienced django coder, he helped me out with some javascript for text entry on the page. He said I didn't need to do it via forms and POST, GET, as the text was just going in with the initial score of 0 and the current datetime.
My issue now is that I cannot get the entered text to save into the database without error.
Since I need to program this in 2-3 weeks and am new to django (and oblivious to javascript, though I've done some Processing with PHP), my questions are;
Am I missing something obvious with the text input save to database?
-and-
Is there a way to have all this using forms and GET, POST in one page
so I can avoid a lot of javascript (unless it is truly easier)?
I am going to start to try to build this with Forms at the moment, but hope for a bit of guidance on best practice from wiser heads.
Here's the code so far:
urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^i/$', 'entries.views.index'),
url(r'^add/(.*)$', 'entries.views.add'),
)
Models.py
from django.db import models
import datetime
from django.utils import timezone
class Entry(models.Model):
text = models.CharField(max_length=15)
score = models.IntegerField(default=0)
pub_date = models.DateTimeField('date published')
def __unicode__(self):
return self.text
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
was_published_recently.admin_order_field = 'pub_date'
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'
index.html
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
</head>
<body>
<ul>
{% for entry in latest_entry_list %}
<li>{{ entry.text }} &nbsp{{ entry.score }}</li>
{% endfor %}
</ul>
<ul>
{% for entry in high_entry_list %}
<li>{{ entry.text }} &nbsp{{ entry.score }}</li>
{% endfor %}
</ul>
<ul>
{% for entry in low_entry_list %}
<li>{{ entry.text }} &nbsp{{ entry.score }}</li>
{% endfor %}
</ul>
<style type="text/css" media="screen">
div h2 span { color: #ff0000; }
div span { color: #00ff00; }
#box { width: 400px; height: 400px; }
#h { color: #ff0000; }
</style>
<h3 id="h">title</h3>
<p>message: {{ text }}</p>
<input type="text" name="word" value="" id="input"/>
<script type="text/javascript" src="{{STATIC_URL}}post.js"></script>
</body>
post.js
console.log("hi from js");
$(document).ready(function() {
$("#input").bind("keypress", function(e) {
//enter key pressed
if (e.keyCode == 13) {
var args = {};
var text = $("#input").val();
$.get("/add/" + text, args).done(function(data) {
console.log("message: " + data);
});
}
});
});
views.py
from django.shortcuts import render
from django.http import HttpResponse
from entries.models import Entry
from django.db import models
import datetime
from django.utils import timezone
def index(request):
context = {
'latest_entry_list': Entry.objects.order_by('-pub_date')[:10],
'high_entry_list': Entry.objects.order_by('-score')[:10],
'low_entry_list': Entry.objects.order_by('score')[:10],
}
return render(request, 'entries/index.html', context);
def add(request, thingtoadd):
#created_date = models.DateTimeField('date published', default=datetime.now)
#created_score = '0'
#created_text = 'test'
#e = Entry(text=created_text, score=created_score,pub_date=created_date)
#e.save()
return HttpResponse('done')
I am unsure of defining the fields for populating the Entry....does the above look right?
I can uncomment the e=Entry(etc...) without error,
but when I uncomment the e.save(), the error is:
GET http://127.0.0.1:8000/add/a 500 (INTERNAL SERVER ERROR) jquery.min.js:4
send jquery.min.js:4
n.extend.ajax jquery.min.js:4
n.(anonymous function) jquery.min.js:4
(anonymous function) post.js:15
n.event.dispatch jquery.min.js:3
r.handle
I will be getting on with trying to do this in forms, but wonder if there is some good advice as to if that is possible - I would ideally like to avoid js extras as I am very unfamiliar with it and it would be another level of unknowns at this point. Any input greatly appreciated...
Your mistake in view function add:
created_date = models.DateTimeField('date published', default=datetime.now)
It must be value assign:
created_date = datetime.now()
Not field definition.
In advance you could specify auto_now_add=True in your model: https://docs.djangoproject.com/en/dev/ref/models/fields/#datefield
In that case field will be filled automatically.
Additional:
It is error in urls.py
You should do some fixes:
urls.py:
url(r'^add/$', 'entries.views.add'),
post.js
$("#input").bind("keypress", function(e) {
//enter key pressed
if (e.keyCode == 13) {
var text = $("#input").val();
var args = {'text': text};
$.get("/add/", args).done(function(data) {
console.log("message: " + data);
});
}
});
views.py
def add(request):
created_date = default=datetime.now()
created_score = '0'
created_text = request.GET.get('text')
e = Entry(text=created_text, score=created_score,pub_date=created_date)
e.save()
return HttpResponse('done')
Update - Solution
The solution in addition to the changes below was to add 'from datetime import datetime' in views....

Categories