Привет, хабровчане. Для будущих студентов курса "Web-разработчик на Python" подготовили перевод материала.
![](https://habrastorage.org/getpro/habr/upload_files/732/15a/e00/73215ae006ff079f65560a27e7b50f83.png)
Если мы хотим передать данные из Django в JavaScript, мы обычно говорим об API, сериализаторах, вызовах JSON и AJAX. Обычно дело усложняется наличием React или Angular на фронте.
Но иногда вам нужно сделать что-то быстро и грязное – построить диаграмму и не задумываться над инфраструктурой одностраничного приложения.
Обычный подход
Допустим, у нас есть приложение на Django со следующей моделью:
from django.db import models
class SomeDataModel(models.Model):
date = models.DateField(db_index=True)
value = models.IntegerField()
И простой TemplateView
:
<img alt="Изображение выглядит как текст
from django.views.generic import TemplateView
class SomeTemplateView(TemplateView):
template_name = 'some_template.html'
Теперь мы можем построить простую линейную диаграмму с помощью Chart.js, и мы не хотим использовать AJAX, создавать новые API и т.д.
Если мы хотим визуализировать простую линейную диаграмму в нашем some_template.html
, код будет выглядеть следующим образом (взято из этих примеров):
<canvas id="chart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js"></script>
<script>
window.onload = function () {
var data = [48, -63, 81, 11, 70];
var labels = ['January', 'February', 'March', 'April', 'May'];
var config = {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'A random dataset',
backgroundColor: 'black',
borderColor: 'lightblue',
data: data,
fill: false
}
]
},
options: {
responsive: true
}
};
var ctx = document.getElementById('chart').getContext('2d');
window.myLine = new Chart(ctx, config);
};
</script>
И получится следующее:
![](https://habrastorage.org/getpro/habr/upload_files/550/3e8/423/5503e84230f138477ccfaff4211d10d9.png)
Теперь, если мы захотим построить диаграмму данных, поступающих из SomeDataModel
, мы подойдем к этой задаче следующим образом:
from django.views.generic import TemplateView
from some_project.some_app.models import SomeDataModel
class SomeTemplateView(TemplateView):
template_name = 'some_template.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['data'] = [
{
'id': obj.id,
'value': obj.value,
'date': obj.date.isoformat()
}
for obj in SomeDataModel.objects.all()
]
return context
А затем мы визуализируем массив JavaScript с помощью шаблона Django:
<canvas id="chart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js"></script>
<script>
window.onload = function () {
// We render via Django template
var data = [
{% for item in data %}
{{ item.value }},
{% endfor %}
]
// We render via Django template
var labels = [
{% for item in data %}
"{{ item.date }}",
{% endfor %}
]
console.log(data);
console.log(labels);
var config = {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'A random dataset',
backgroundColor: 'black',
borderColor: 'lightblue',
data: data,
fill: false
}
]
},
options: {
responsive: true
}
};
var ctx = document.getElementById('chart').getContext('2d');
window.myLine = new Chart(ctx, config);
};
</script>
Именно так это и работает, но, как по мне, слишком грязно. У нас больше нет JavaScript, но есть JavaScript с шаблоном Django. Мы теряем возможность выделить JavaScript в отдельный файл .js. Также мы не можем красиво работать с этим JavaScript.
Но можем сделать лучше и быстрее.
Добавим остроты
Стратегия следующая:
В нашем случае мы будем сериализовать данные через
json.dumps
и хранить их в контексте.Отрендерим скрытый элемент
<div>
с уникальнымid
и атрибутомdata-json
, а именно с сериализованными данными JSON.Запросите этот
<div>
из JavaScript, прочитайте атрибутdata-json
и используйтеJSON.parse
, чтобы получить необходимые данные.
Преимуществом становится то, что код на JavaScript не содержит шаблонов Django, но имеет переиспользуемый шаблон для получения необходимых данных.
Почти как упрощенный AJAX.
![](https://habrastorage.org/getpro/habr/upload_files/5f0/993/b98/5f0993b988f28749bf45aba46db42650.png)
Ниже пример того, как я использую эту стратегию:
import json
from django.views.generic import TemplateView
class SomeTemplateView(TemplateView):
template_name = 'some_template.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['data'] = json.dumps(
[
{
'id': obj.id,
'value': obj.value,
'date': obj.date.isoformat()
}
for obj in SomeDataModel.objects.all()
]
)
return context
Теперь извлечем наш код на JavaScript в статичный файл chart.js
.
В результате получим some_template.html
:
{% load static %}
<div style="display: none" id="jsonData" data-json="{{ data }}"></div>
<canvas id="chart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js"></script>
<script src="{% static 'chart.js' %}"></script>
Как видите в скрытом div
происходит магия. Мы скрываем div
, чтобы удалить его из любой верстки. Здесь вы можете дать ему соответствующий id
или использовать любой подходящий HTML-элемент.
Атрибут data-json
(который необязателен и не является чем-то предопределенным) содержит нужный нам JSON.
Теперь, наконец, мы реализуем следующую простую функцию для получения и анализа необходимых нам данных:
function loadJson(selector) {
return JSON.parse(document.querySelector(selector).getAttribute('data-json'));
}
И вот наш chart.js
готов:
function loadJson(selector) {
return JSON.parse(document.querySelector(selector).getAttribute('data-json'));
}
window.onload = function () {
var jsonData = loadJson('#jsonData');
var data = jsonData.map((item) => item.value);
var labels = jsonData.map((item) => item.date);
console.log(data);
console.log(labels);
var config = {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'A random dataset',
backgroundColor: 'black',
borderColor: 'lightblue',
data: data,
fill: false
}
]
},
options: {
responsive: true
}
};
var ctx = document.getElementById('chart').getContext('2d');
window.myLine = new Chart(ctx, config);
};
Быстрый и грязный способ для тех, кто просто хочет что-то отправить. А вот и конечный результат:
![](https://habrastorage.org/getpro/habr/upload_files/956/e8f/559/956e8f559e9c668dda6d1083cb85db2b.png)
Дисклеймер
Я не рекомендую вам пользоваться этим подходом для чего-то большего, чем грязное доказательство концепции. Как только ваш код на JavaScript начнет расти, станет сложно его контролировать. Рекомендуется пройти путь SPA с одним из популярных фреймворков (в нашем случае React).
Узнать подробнее о курсе "Web-разработчик на Python".
Посетить Demo day к курсу.