Pull to refresh
1056.27
OTUS
Цифровые навыки от ведущих экспертов

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

Reading time4 min
Views18K
Original author: Radoslav Georgiev

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

Tags:
Hubs:
Total votes 13: ↑9 and ↓4+5
Comments1

Articles

Information

Website
otus.ru
Registered
Founded
Employees
101–200 employees
Location
Россия
Representative
OTUS