Привет, хабровчане. Для будущих студентов курса "Web-разработчик на Python" подготовили перевод материала.
Если мы хотим передать данные из 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>
И получится следующее:
Теперь, если мы захотим построить диаграмму данных, поступающих из 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.
Ниже пример того, как я использую эту стратегию:
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);
};
Быстрый и грязный способ для тех, кто просто хочет что-то отправить. А вот и конечный результат:
Дисклеймер
Я не рекомендую вам пользоваться этим подходом для чего-то большего, чем грязное доказательство концепции. Как только ваш код на JavaScript начнет расти, станет сложно его контролировать. Рекомендуется пройти путь SPA с одним из популярных фреймворков (в нашем случае React).
Узнать подробнее о курсе "Web-разработчик на Python".
Посетить Demo day к курсу.
baldr
Ну зачем в атрибутах-то? Начиная с версии 2.1 есть же json_script
И получаем:
Результат получаем как: