Шаблонные переменные Django и Javascript


219

Когда я отрисовываю страницу, используя шаблонизатор Django, я могу передать словарную переменную, содержащую различные значения, чтобы манипулировать ими на странице с помощью {{ myVar }}.

Есть ли способ получить доступ к той же переменной в Javascript (возможно, используя DOM, я не знаю, как Django делает переменные доступными)? Я хочу иметь возможность искать детали с помощью поиска AJAX на основе значений, содержащихся в переданных переменных.

Ответы:


292

{{variable}}Заменяется непосредственно в HTML. Сделать вид источника; это не «переменная» или что-то подобное. Это просто визуализированный текст.

Сказав это, вы можете поместить такую ​​замену в свой JavaScript.

<script type="text/javascript"> 
   var a = "{{someDjangoVariable}}";
</script>

Это дает вам «динамический» JavaScript.


29
Обратите внимание, что согласно этому решению это уязвимо для инъекционных атак
— Casebash

13
@Casebash: Для таких случаев escapejsсуществует фильтр:escapejs('<>') -> u'\\u003C\\u003E'
— Томаш Зелиньски

24
Просто добавьте к этому для справки: если «someDjangoVariable», как оказалось, JSON, обязательно используйте {{someDjangoVariable | safe}}, чтобы удалить & quot;
— Отметить

4
Этот ответ работает только для простой переменной, он не работает для сложной структуры данных. В этом случае самое простое решение - добавить код на стороне клиента для обхода структуры данных и создать аналогичный код в Javascript. Если сложная структура данных имеет формат JSON, другим решением является ее сериализация, передача сериализованного JSON в шаблон Django в коде на стороне сервера и десериализация JSON в объекте javascript в коде на стороне клиента. Один ответ ниже упоминает эту альтернативу.
— Алан Евангелиста

14
Что делать, если JavaScript написан в другом файле?
— the_unknown_spirit

64

ВНИМАНИЕ! Проверьте билет # 17419 для обсуждения добавления подобного тега в ядро ​​Django и возможных уязвимостей XSS, появившихся при использовании этого тега шаблона с данными, созданными пользователем. Комментарий от amacneil обсуждает большинство проблем, поднятых в билете.


Я думаю, что наиболее гибкий и удобный способ сделать это - определить шаблонный фильтр для переменных, которые вы хотите использовать в коде JS. Это позволяет вам гарантировать, что ваши данные правильно экранированы, и вы можете использовать их со сложными структурами данных, такими как dictи list. Вот почему я пишу этот ответ, несмотря на то, что есть принятый ответ с большим количеством голосов.

Вот пример шаблона шаблона:

// myapp/templatetags/js.py

from django.utils.safestring import mark_safe
from django.template import Library

import json


register = Library()


@register.filter(is_safe=True)
def js(obj):
    return mark_safe(json.dumps(obj))

Этот шаблон фильтров преобразует переменную в строку JSON. Вы можете использовать это так:

// myapp/templates/example.html

{% load js %}

<script type="text/javascript">
    var someVar = {{ some_var | js }};
</script>

3
Это хорошо, потому что он позволяет копировать только некоторые входные переменные шаблона Django в Javascript, и код на стороне сервера не должен знать, какие структуры данных должны использоваться Javascript и, следовательно, преобразовываться в JSON перед рендерингом шаблона Django. Либо используйте это, либо всегда копируйте все переменные Django в Javascript.
— Алан Евангелиста


1
@JorgeOrpinel Нет, это не то же самое. safeтолько оценивает значение как безопасное, без надлежащего преобразования и экранирования.
— Ярослав Админ

2
Как вы тогда отображаете переменную в шаблоне django?
— kbdev

1
@ Санди, когда я публиковал его, было обычным иметь виджет в отдельном файле JS и инициализировать его в исходном коде страницы. Допустим, вы объявляете function myWidget(config) { /* implementation */ }в файле JS, а затем используете его на некоторых страницах myWidget({{ pythonConfig | js }}). Но вы не можете использовать его в файлах JS (как вы заметили), поэтому он имеет свои ограничения.
— Ярослав Админ

51

Решение, которое сработало для меня, заключается в использовании скрытого поля ввода в шаблоне

<input type="hidden" id="myVar" name="variable" value="{{ variable }}">

Затем получить значение в JavaScript таким образом,

var myVar = document.getElementById("myVar").value;

3
будь осторожен, хотя. в зависимости от того, как вы используете переменную / форму, пользователь может добавить что угодно.
— AndyL

3
Вы также можете установить поле ввода только для чтения (см. эту ссылку w3schools.com/tags/att_input_readonly.asp )
— nu everest

Если это что-то, что не изменит базу данных или не будет отправлено в запрос к базе данных, это было бы хорошо. @AndyL
— James111

3
Ребята ... пользователи могут делать то, что они хотят в любом случае. Браузеры делают это так легко с помощью полнофункционального инспектора DOM и инструментов отладки. Мораль этой истории: ВСЕ вы проверяете данные на сервере .
— user2867288

1
Кто-нибудь может подсказать, как вы получите доступ к этой переменной во внешнем файле JavaScript?
— Ахтишам

27

По состоянию на Django 2.1, новый встроенный в шаблонный тег был введен специально для этого случая использования: json_script.

https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#json-script

Новый тег безопасно сериализует значения шаблона и защищает от XSS.

Отрывок из Django Docs:

Безопасно выводит объект Python в виде JSON, завернутый в тег и готовый для использования с JavaScript.


10

Вот что я делаю очень легко: я изменил файл base.html для своего шаблона и поместил его внизу:

{% if DJdata %}
    <script type="text/javascript">
        (function () {window.DJdata = {{DJdata|safe}};})();
    </script>
{% endif %}

затем, когда я хочу использовать переменную в файлах javascript, я создаю словарь DJdata и добавляю его в контекст с помощью json: context['DJdata'] = json.dumps(DJdata)

Надеюсь, поможет!


Не используйте это, это уязвимо для внедрения скриптов (XSS).
— pcworld

8

Для словаря лучше всего сначала кодировать в JSON. Вы можете использовать simplejson.dumps () или, если вы хотите конвертировать из модели данных в App Engine, вы можете использовать encode () из библиотеки GQLEncoder.


1
Обратите внимание, что, начиная с django 1.7, «simplejson» стал «json».
— Fiveclubs 21.09.15

4

Я столкнулся с вопросом о подобии, и ответ, предложенный С. Лоттом, работал на меня.

<script type="text/javascript"> 
   var a = "{{someDjangoVariable}}"
</script>

Тем не менее, я хотел бы указать на существенное ограничение реализации здесь. Если вы планируете поместить свой код JavaScript в другой файл и включить этот файл в свой шаблон. Это не сработает.

Это работает только тогда, когда вы используете основной шаблон и код JavaScript в одном файле. Вероятно, команда Django может решить эту проблему.


1
Как мы можем преодолеть это?
— Чаба Тот

2
Чтобы преодолеть это, вы можете разместить глобальные переменные Javascript, как показано в примере перед тем, как включить статический файл Javascript. Таким образом, ваш статический файл Javascript будет иметь доступ ко всем глобальным переменным.
— Bobort

Не используйте это, это уязвимо для внедрения скриптов (XSS).
— pcworld

Они могут проверять данные на стороне сервера.
— Мухаммед Файзан Фарид

4

Я тоже боролся с этим. На первый взгляд кажется, что вышеуказанные решения должны работать. Тем не менее, архитектура django требует, чтобы каждый html-файл имел свои собственные визуализированные переменные (то {{contact}}есть визуализируется contact.html, пока {{posts}}идет, например, index.htmlи так далее). С другой стороны, <script>теги появляются после {%endblock%}in, base.htmlот которого contact.htmlи index.htmlнаследуются. Это в основном означает, что любое решение, включая

<script type="text/javascript">
    var myVar = "{{ myVar }}"
</script>

обязательно потерпит неудачу, потому что переменная и скрипт не могут сосуществовать в одном и том же файле.

Простое решение, которое я в конечном итоге придумал и сработало для меня, заключалось в том, чтобы просто обернуть переменную тегом с идентификатором, а затем обратиться к нему в файле js, например, так:

// index.html
<div id="myvar">{{ myVar }}</div>

а потом:

// somecode.js
var someVar = document.getElementById("myvar").innerHTML;

и просто включить <script src="static/js/somecode.js"></script>в , base.htmlкак обычно. Конечно, речь идет только о получении контента. Что касается безопасности, просто следуйте другим ответам.


Возможно, вы захотите использовать .textContentвместо .innerHTML, потому что в противном случае сущности, которые закодированы в HTML, также будут частью переменной JS. Но даже тогда это не может быть воспроизведено 1: 1 (я не уверен).
— pcworld

Уточнение . Я использую аналогичный способ для захвата значений полей в переменных (используя идентификаторы, динамически создаваемые в форме), и это работает (но только для ОДНОЙ строки набора форм ). То, что я не могу обойти, это захват значений из всех строк полей набора форм , которые заполняются вручную (т.е. в HTML-таблице с использованием цикла for ). Поскольку вы будете визуализировать, переменные только последней строки набора форм передаются переменным, а значения перед этим перезаписываются последними значениями по мере того, как цикл for проходит через строки набора форм. Есть ли способ обойти это?
— user12379095

3

Для объекта JavaScript, хранящегося в поле Django в виде текста, который должен снова стать объектом JavaScript, динамически вставляемым в сценарий на странице, вам необходимо использовать escapejsи JSON.parse():

var CropOpts = JSON.parse("{{ profile.last_crop_coords|escapejs }}");

Django escapejsправильно обрабатывает цитирование и JSON.parse()преобразует строку обратно в объект JS.


2

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

<script type="text/javascript">
    var myVar = "{{ myVar }}"
</script>

<script type="text/javascript" src="{% static "scripts/my_script.js" %}"></script>

data определяется в представлении, как обычно в get_context_data

def get_context_data(self, *args, **kwargs):
    context['myVar'] = True
    return context

Часть, чтобы объявить переменную глобально, была фактически полезна.
— Рахул

если вы рендерите данные в переменные js из шаблонов, как указано выше, то должны отображаться на той же странице (сделать их глобальными), а другой код будет идти в отдельный файл js. На стороне сервера должны быть действительные данные, потому что пользователь может перейти из браузера.
— Мухаммед Файзан Фарид

1

Я использую этот способ в Django 2.1 и работаю для меня, и этот способ безопасен (ссылка) :

Сторона Джанго:

def age(request):
    mydata = {'age':12}
    return render(request, 'test.html', context={"mydata_json": json.dumps(mydata)})

Html сторона:

<script type='text/javascript'>
     const  mydata = {{ mydata_json|safe }};
console.log(mydata)
 </script>

0

вы можете собрать весь скрипт, где ваша переменная массива объявлена ​​в строке, следующим образом:

views.py

    aaa = [41, 56, 25, 48, 72, 34, 12]
    prueba = "<script>var data2 =["
    for a in aaa:
        aa = str(a)
        prueba = prueba + "'" + aa + "',"
    prueba = prueba + "];</script>"

это будет генерировать строку следующим образом

prueba = "<script>var data2 =['41','56','25','48','72','34','12'];</script>"

получив эту строку, вы должны отправить ее в шаблон

views.py

return render(request, 'example.html', {"prueba": prueba})

в шаблоне вы получаете его и интерпретируете его литературным способом как код htm, непосредственно перед кодом javascript, где он вам нужен, например

шаблон

{{ prueba|safe  }}

и ниже, это остальная часть вашего кода, имейте в виду, что переменная для использования в примере - data2

<script>
 console.log(data2);
</script>

таким образом, вы будете сохранять тип данных, который в этом случае является договоренностью


Используйте это только в том случае, если вы уверены, что оно aaaбудет содержать только цифры, в противном случае возможен XSS (внедрение скрипта).
— pcworld

0

В Javascript у меня сработали две вещи:

'{{context_variable|escapejs }}'

и другое: в views.py

from json import dumps as jdumps

def func(request):
    context={'message': jdumps('hello there')}
    return render(request,'index.html',context)

и в HTML:

{{ message|safe }}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.