
Многие веб-разработчики время от времени сталкиваются с необходимостью визуализировать сравнительно большое количество данных при помощи диаграмм (далее я буду называть их графиками, хоть это и не совсем верно). Задача не нова, и в сети есть множество готовых решений: работающие на стороне сервера и на стороне клиента, использующие изображения, Canvas, SVG, Flash, Silverlight…
В этой статье я расскажу про django-google-charts и некоторые особенности использования Google Chart Tools для построения графиков на сайте под управлением Django.
Часто, когда нужно добавить график на страницу, разработчик идет по пути наименьшего сопротивления: копирует JavaScript из примера в интернете и как-нибудь выводит в него данные из приложения. Получается что-то наподобие:
var chart_data = [
{% for row in chart_data %}
[{{ row.0 }}, {{ row.1 }}],
{% endfor %}
];
Почему это плохо?
- Код тяжело читается, вследствие чего легко допустить ошибку. Вы же заметили ошибку в моем примере? :)
- Сложно масштабировать. Когда нужно добавить второй график на страницу, обычно копируют этот же кусок кода еще раз и меняют имена переменных. В результате получается нечитаемая каша, отлаживать это и вносить изменения достаточно неприятно.
- Сильное связывание с контекстом. Повторное использование такого шаблона ограничено.
Постановка задачи
Хорошее решение для рисования графиков:
- Позволяет просто и понятно добавлять новые графики на сайт и убирать ненужные;
- Соблюдает принцип DRY для определения общих элементов (например, цветовой схемы);
- Можно повторно использовать;
- Не изобретает нового API.
Вариант решения
Приложение для компоновки графиков django-google-charts выросло из небольшого набора хаков в моем текущем проекте. Алярма: это первый публичный релиз, возможны причудливые баги. Исправления и замечания всячески приветствуются.
Установка
$ pip install django-google-charts
# или
$ easy_install django-google-charts
Добавляем
'googlecharts'
в INSTALLED_APPS
.Как это работает (общий вид)
{% load googlecharts %}
{% googlecharts %}
{% data переменная "необязательное имя" %} {# (именованный) набор данных #}
{% col "тип" "название" %}...{% endcol %} {# формат #}
{% col "тип" "название" %}...{% endcol %} {# еще формат #}
...
{% enddata %}
{% options "необязательное имя" %} {# (именованный) набор опций #}
...
{% endoptions %}
{% graph "#id" "данные" "опции" %} {# точка сборки #}
{% endgooglecharts %}
Расскажу немного про назначение каждого тега.
{% googlecharts %}...{% endgooglecharts %}
Подключает необходимые скрипты, является контейнером для всей конструкции. Ничего интересного.
{% data переменная "имя" %}...{% enddata %}
Именованный набор данных. Имя можно не указывать (получится
"default"
).{% col "тип" "название" %}...{% endcol %}
Формат. Типы данных в Google Visualization API вот такие:
'string' 'number' 'boolean' 'date' 'datetime' 'timeofday'
Подробнее можно почитать в документации.
Внутрь тега передается специальная переменная
val
, в этом месте ее можно форматировать:{% col "string" "Date" %}'{{ val|date:"M j" }}'{% endcol %}
{# или, например #}
{% col "number" %}{{ val|floatformat:2 }}{% endcol %}
Результат должен соответствовать заявленному типу; кавычки вокруг строки сами не поставятся.
Пример. Допустим, в блок
{% data %}
мы передали такую переменную контекста:[['foo', 32], ['bar', 64], ['baz', 96]]
Нам нужны два тега
{% col %}
, по количеству элементов в строке входных данных. Первый получит на вход 'foo', 'bar' и 'baz'; второй, соответственно, 32, 64 и 96. Реализация (самая простая) может выглядеть так:{% col "string" "Name" %}"{{ val }}"{% endcol %}
{% col "number" "Value" %}{{ val }}{% endcol %}
{% options "имя" %}...{% endoptions %}
Параметры графика.
{% options %}
kind: "LineChart",
options: {
width: 300,
height: 240
// ...
}
{% endoptions %}
Внутри тега объект JavaScript, можно использовать глобальные переменные и вызывать функции. Типы графиков и поддерживаемые опции для каждого типа перечислены вот здесь.
{% graph "id_элемента" "данные" "опции" %}
Выводим график в элемент на странице. Последние два параметра можно не указывать, получится
"default" "default"
.Пример использования
Предположим, у нас есть такая модель:
class Payment(models.Model):
amount = models.DecimalField(max_digits=11, decimal_places=4)
datetime = models.DateTimeField()
Для подготовки данных к отображению в виде графика можно использовать django-qsstats-magic.
from qsstats import QuerySetStats
def view_func(request):
start_date = ...
end_date = ...
queryset = Payment.objects.all()
# считаем количество платежей...
qsstats = QuerySetStats(queryset, date_field='datetime', aggregate=Count('id'))
# ...в день за указанный период
values = qsstats.time_series(start_date, end_date, interval='days')
return render_to_response('template.html', {'values': values})
Метод
time_series
возвращает данные в таком виде:[[date, value], [date, value], ...]
В файле template.html:
{% load googlecharts %}
<div id="count_graph"></div>
{% googlecharts %}
{% data values "count" %}
{% col "string" "Date" %}"{{ val|date:"M j" }}"{% endcol %}
{% col "number" "# of payments" %}{{ val }}{% endcol %}
{% enddata %}
{% options %}
kind: "LineChart",
options: {
backgroundColor: "#f9f9f9",
colors: ["#09f"],
gridlineColor: "#ddd",
legend: "none",
vAxis: {minValue: 0},
chartArea: {left: 40, top: 20, width: 240, height: 180},
width: 300,
height: 240
}
{% endoptions %}
{% graph "count_graph" "count" %} {# используем опции по умолчанию #}
{% endgooglecharts %}
Масштабирование
Теперь, чтобы добавить еще один график на страницу, нужно сделать следующее:
– Собрать данные (views.py):
# сумма всех платежей в день за указанный период
summary = qsstats.time_series(start_date, end_date, interval='days', aggregate=Sum('amount'))
return render_to_response('template.html', {'values': values, 'summary': summary})
– Добавить в template.html:
<div id="count_sum"></div>
...
{% data summary "sum" %}
{% col "string" "Date" %}"{{ val|date:"M j" }}"{% endcol %}
{% col "number" "Paid amount, USD" %}{{ val|floatformat:2 }}{% endcol %}
{% enddata %}
...
{% graph "count_sum" "sum" %} {# используем опции по умолчанию, снова #}
GitHub
Исходный код django-google-charts содержит также демонстрационный проект. Чтобы его запустить, достаточно выполнить:
$ python manage.py syncdb --noinput # создает базу данных sqlite в /tmp
$ python manage.py populatedb # наполняет ее случайными данными
$ python manage.py runserver
Выглядит тестовый проект вот так:

Ограничения первого публичного релиза (ложка дегтя)
- Только один тег
{% googlecharts %}
на странице; - (Почти) нет проверки ошибок, особенно в JavaScript;
- Исчезающе мало документации.