Быстрый и грязный Django — Передача данных в JavaScript без AJAX

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет, хабровчане. Для будущих студентов курса "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.

Но можем сделать лучше и быстрее.

Добавим остроты

Стратегия следующая:

  1. В нашем случае мы будем сериализовать данные через json.dumps и хранить их в контексте.

  2. Отрендерим скрытый элемент <div> с уникальным id и атрибутом data-json, а именно с сериализованными данными JSON. 

  3. Запросите этот <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 к курсу.

Источник: https://habr.com/ru/company/otus/blog/547722/


Интересные статьи

Интересные статьи

Управление миграциями баз данных для нескольких сред и команд может быть достаточно сложной задачей. В этой статье описывается, как сочетание Git, контейнеров и клонов ба...
Библиотека pandas — это один из лучших инструментов для разведочного анализа данных. Но это не означает, что pandas — это универсальное средство, подходящее для решения любых задач. В частности, ...
Этот пост будет из серии, об инструментах безопасности, которые доступны в Битриксе сразу «из коробки». Перечислю их все, скажу какой инструмент в какой редакции Битрикса доступен, кратко и не очень р...
Продолжая серию заметок о менее известных функциях bash, покажу вам семь переменных, о которых вы могли не знать. 1) PROMPT_COMMAND Возможно, вы уже в курсе, как манипулировать приглашением p...
Автокэширование в 1с-Битрикс — хорошо развитая и довольно сложная система, позволяющая в разы уменьшить число обращений к базе данных и ускорить выполнение страниц.