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

219

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

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

AlMcLean
источник

Ответы:

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.
Алан Евангелиста
Но обратите внимание: stackoverflow.com/questions/23752156/…
Сиро Сантилли 法轮功 冠状 病 六四 事件 法轮功
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;
bosco-
источник
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)

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

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

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

jamtoday
источник
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.

shacker
источник
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>
Henrry
источник
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 }}
MbeforeL
источник