Передача данных Python в JavaScript через Django

Я использую Django и Apache для обслуживания веб-страниц. Мой код JavaScript в настоящее время включает в себя объект данных со значениями, которые будут отображаться в различных HTML-виджетах на основе выбора пользователя из меню выбора. Я хочу получить эти данные из словаря Python. Я думаю, что знаю, как встроить код JavaScript в HTML, но как мне встроить объект данных в этот скрипт (на лету), чтобы функции скрипта могли его использовать?

Другими словами, я хочу создать объект JavaScript или массив из словаря Python, затем вставить этот объект в код JavaScript, а затем вставить этот код JavaScript в HTML.

Я предполагаю, что эта структура (например, данные, встроенные в переменные в коде JavaScript) является неоптимальной, но как новичок я не знаю альтернатив. Я видел рецензии на функции сериализации Django, но они не помогают мне, пока я не получу данные в свой код JavaScript.

Я (пока) не использую библиотеку JavaScript, такую ​​как jQuery.

8 ответов

Решение

NB см. 2018 обновление внизу

Я рекомендую не добавлять много JavaScript в ваши шаблоны Django - его сложно писать и отлаживать, особенно по мере расширения вашего проекта. Вместо этого попробуйте написать весь свой JavaScript в отдельном файле сценария, который загружает ваш шаблон, и просто включить в шаблон только объект данных JSON. Это позволяет вам выполнять такие вещи, как запускать все ваше приложение JavaScript через что-то вроде JSLint, минимизировать его и т. Д., И вы можете протестировать его в статическом HTML-файле без каких-либо зависимостей от вашего приложения Django. Использование такой библиотеки, как simplejson, также экономит время, затрачиваемое на написание утомительного кода сериализации.

Если вы не предполагаете, что создаете приложение AJAX, это может быть сделано просто так:

По мнению:

from django.utils import simplejson


def view(request, …):
    js_data = simplejson.dumps(my_dict)
    …
    render_template_to_response("my_template.html", {"my_data": js_data, …})

В шаблоне:

<script type="text/javascript">
    data_from_django = {{ my_data }};
    widget.init(data_from_django);
</script>

Обратите внимание, что тип данных имеет значение: если my_data это простое число или строка из контролируемого источника, который не содержит HTML, например форматированная дата, никакой специальной обработки не требуется. Если возможно получить ненадежные данные, предоставленные пользователем, вам необходимо очистить их с помощью чего-то вроде фильтров escape или escapejs и убедиться, что ваш JavaScript обрабатывает данные безопасно, чтобы избежать атак межсайтового скриптинга.

Что касается дат, вы можете подумать о том, как вы проводите даты. Мне почти всегда было проще передать их как метки времени Unix:

В Джанго:

time_t = time.mktime(my_date.timetuple())

В JavaScript, если вы сделали что-то вроде time_t = {{ time_t }} с результатами фрагмента выше:

my_date = new Date();
my_date.setTime(time_t*1000);

Наконец, обратите внимание на UTC - вам нужно, чтобы функции даты Python и Django обменивались данными в UTC, чтобы избежать смущающих сдвигов от местного времени пользователя.

РЕДАКТИРОВАТЬ: Обратите внимание, что setTime в javascript находится в миллисекундах, тогда как вывод time.mktime равен секундам. Вот почему нам нужно умножить на 1000

Обновление 2018 года. Мне по-прежнему нравится JSON для сложных значений, но за прошедшее десятилетие API данных HTML5 достиг почти универсальной поддержки браузера, и это очень удобно для передачи простых значений (не в списке / dict), особенно если вы хотите использовать CSS правила применяются на основе этих значений, и вам не нужны неподдерживаемые версии Internet Explorer.

<div id="my-widget" data-view-mode="tabular">…</div>

let myWidget = document.getElementById("my-widget");
console.log(myWidget.dataset.viewMode); // Prints tabular
somethingElse.addEventListener('click', evt => {
    myWidget.dataset.viewMode = "list";
});

Это отличный способ представить данные в CSS, если вы хотите установить начальное состояние просмотра в шаблоне Django и автоматически обновлять его, когда JavaScript обновляет data- приписывать. Я использую это для таких вещей, как скрытие виджета прогресса до тех пор, пока пользователь не выберет что-либо для обработки или для условного отображения / скрытия ошибок на основе результатов выборки или даже для чего-то вроде отображения количества активных записей с использованием CSS, например #some-element::after { content: attr(data-active-transfers); },

Для тех, у кого могут быть проблемы с этим, убедитесь, что вы отображаете свой объект json в безопасном режиме в шаблоне. Вы можете вручную установить это так

<script type="text/javascript">
    data_from_django = {{ my_data|safe }};
    widget.init(data_from_django);
</script>

Начиная с середины 2018 года самый простой подход заключается в использовании json-модуля Python, теперь simplejson устарела. Помните, что, как упоминает @wilblack, вы должны предотвратить автоэкранирование Django, используя safe фильтр или autoescape пометить с off вариант. В обоих случаях в представлении вы добавляете содержимое словаря в контекст

viewset.py

import json def get_context_data(self, **kwargs): context['my_dictionary'] = json.dumps(self.object.mydict)

а затем в шаблон, который вы добавляете, как предложил @wilblack:

template.html

<script> my_data = {{ my_dictionary|safe }}; </script>

Вы можете включить <script> теги внутри ваших шаблонов.html, а затем создавать структуры данных, как вам удобно. Язык шаблонов предназначен не только для HTML, но и для литералов объектов Javascript.

И Пол прав: возможно, лучше всего использовать модуль json для создания строки JSON, а затем вставить эту строку в шаблон. Это лучше всего решит проблемы цитирования и с легкостью справится с глубокими структурами.

Это неоптимально. Рассматривали ли вы передачу своих данных в формате JSON, используя для этого встроенный сериализатор django?

Устранение дыры в безопасности в ответах @willblack и @Daniel_Kislyuk.

Если данные не являются надежными, вы не можете просто сделать

viewset.py

       def get_context_data(self, **kwargs):
    context['my_dictionary'] = json.dumps(self.object.mydict)

template.html

      <script>
    my_data = {{ my_dictionary|safe }};
</script>

потому что данные могут быть чем-то вроде {"</script><script>alert(123);</script>":""}и косые черты по умолчанию не экранируются. Ясно, что побег json.dumps может не на 100% соответствовать экранированию в Javascript, откуда и возникают проблемы.

Фиксированное решение

Насколько я могу судить, следующее устраняет проблему:

      <script>
   my_data = JSON.parse("{{ my_dictionary|escapejs }}");
</script>

Если все еще есть проблемы, пишите в комментариях.

Встраивание Java Script в шаблон Django - это всегда плохая идея.

Скорее, потому что есть некоторые исключения из этого правила.

Все зависит от вашего сайта кода Java Script и функциональности.

Лучше иметь отдельные статические файлы, такие как JS, но проблема в том, что каждый отдельный файл нуждается в другом механизме подключения /GET/ запроса / ответа. Иногда, для небольшого кода, два кода кода или JS помещают это в шаблон, тогда используется механизм шаблонных тегов django - вы можете использовать его в других шаблонах;)

Про объекты - то же самое. Если у вашего сайта есть AJAX construction/web2.0, как одолжение - вы можете добиться очень хорошего эффекта, применяя некоторые операции подсчета / математики на стороне клиента. Если объекты маленькие - встроенные в шаблон, если большие - ответьте на них в другом соединении, чтобы избежать зависания страницы для пользователя.

Смотрите соответствующий ответ на этот вопрос. Одним из вариантов является использование jsonpickle для сериализации между объектами Python и объектами JSON/Javascript. Он упаковывает simplejson и обрабатывает вещи, которые обычно не принимаются simplejson.

Другие вопросы по тегам