Как передать данные из Flask в JavaScript в шаблоне?

Мое приложение обращается к API, который возвращает словарь. Я хочу передать информацию из этого dict в JavaScript в представлении. Я использую API Карт Google в JS, в частности, поэтому я хотел бы передать ему список кортежей с информацией long / lat. Я знаю, что render_template передаст эти переменные в представление, чтобы их можно было использовать в HTML, но как я могу передать их в JavaScript в шаблоне?

from flask import Flask
from flask import render_template

app = Flask(__name__)

import foo_api

api = foo_api.API('API KEY')

@app.route('/')
def get_data():
    events = api.call(get_event, arg0, arg1)
    geocode = event['latitude'], event['longitude']
    return render_template('get_data.html', geocode=geocode)

person mea    schedule 24.06.2012    source источник


Ответы (9)


Вы можете использовать {{ variable }} в любом месте вашего шаблона, а не только в части HTML. Итак, это должно работать:

<html>
<head>
  <script>
    var someJavaScriptVar = '{{ geocode[1] }}';
  </script>
</head>
<body>
  <p>Hello World</p>
  <button onclick="alert('Geocode: {{ geocode[0] }} ' + someJavaScriptVar)" />
</body>
</html>

Думайте об этом как о двухэтапном процессе: во-первых, Jinja (механизм шаблонов, который использует Flask) генерирует ваш текстовый вывод. Это отправляется пользователю, который выполняет видимый им JavaScript. Если вы хотите, чтобы ваша переменная Flask была доступна в JavaScript в виде массива, вам необходимо сгенерировать определение массива в вашем выводе:

<html>
  <head>
    <script>
      var myGeocode = ['{{ geocode[0] }}', '{{ geocode[1] }}'];
    </script>
  </head>
  <body>
    <p>Hello World</p>
    <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
  </body>
</html>

Jinja также предлагает более сложные конструкции из Python, поэтому вы можете сократить его до:

<html>
<head>
  <script>
    var myGeocode = [{{ ', '.join(geocode) }}];
  </script>
</head>
<body>
  <p>Hello World</p>
  <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
</body>
</html>

Вы также можете использовать for циклы, if операторы и многое другое, дополнительные сведения см. В документации Jinja2.

Также обратите внимание на ответ Форда, который указывает на фильтр tojson, который является дополнение к стандартному набору фильтров Jinja2.

Редактировать ноябрь 2018 г .: tojson теперь включен в стандартный набор фильтров Jinja2.

person mensi    schedule 24.06.2012
comment
Очень признателен, менси! Это было моей первоначальной мыслью, но документация по Flask не дает ясно понять, что вы также можете использовать форму {{var}} в JS. Спасибо, что прояснили это. - person mea; 24.06.2012
comment
@mea: вы также можете использовать механизм шаблонов для создания произвольных текстовых файлов, я также использовал его для динамического создания файлов TeX (- ›PDF) и электронной почты, он довольно универсален;) - person mensi; 24.06.2012
comment
быстрый последующий вопрос: если я выполняю цикл for в JS, могу ли я использовать индексную переменную в переменной python, например. {{геокодировать [i]}}? - person mea; 25.06.2012
comment
В этом есть смысл, но решение, которое вы опубликовали, похоже, требует, чтобы я вручную кодировал содержимое массива JS. Я надеялся, что смогу сделать это более программно, так что я смогу передать список Python переменной длины, а цикл JS for может выполнять итерацию по длине, а затем добавлять их в массив JS. Извините, если я недостаточно ясно выражаюсь, но я довольно зеленый для JS и веб-разработчиков. - person mea; 25.06.2012
comment
Вау, круто! Я думаю, эта вставка не работает в файле javascript, включенном в шаблон? - person Felix; 24.09.2013
comment
Только если вы тоже подаете файл через флягу. Я обычно стараюсь сохранять внешние файлы статичными и предоставлять пользовательские данные через большой объект javascript в стиле json в качестве входных данных для функций во внешнем файле ... - person mensi; 24.09.2013
comment
Это безопасно? Скажем, я хотел передать currentUserCanViewXYZ Boolean из Flask таким образом, а затем использовать JavaScript для отображения / скрытия XYZ. Смог бы пользователь тогда просто отредактировать DOM, чтобы изменить, могут ли они просматривать XYZ, или это не работает так? Под JavaScript я действительно имею в виду реакцию, если это имеет значение. - person Jake Stockwin; 28.07.2017
comment
@JakeStockwin, если вы хотите скрыть части, вам нужно использовать {{if ...}} и т. Д. В шаблоне, чтобы гарантировать, что контент никогда не генерируется. Все, что вы отправляете клиенту, может видеть клиент. - person mensi; 31.07.2017
comment
Можете ли вы передать их в отдельный файл javascript? Кодировать скрипт внутри html-файла кажется сомнительным. - person Rocket Pingu; 16.03.2018
comment
@RocketPingu, если вы хотите передать данные в отдельном файле, обычно проще просто использовать jsonmodule, чтобы выгрузить ваши данные в виде объекта json - person mensi; 17.03.2018
comment
однако, если геокодирование - это список кортежей, содержащих данные, как сделать цикл, который проходит через каждый кортеж в списке? - person ; 12.07.2018
comment
| safe также может быть полезен в зависимости от того, как отформатирована ваша переменная. - person jimh; 07.12.2020

Идеальный способ превратить практически любой объект Python в объект JavaScript - использовать JSON. JSON - отличный формат для передачи данных между системами, но иногда мы забываем, что он означает JavaScript Object Notation. Это означает, что внедрение JSON в шаблон аналогично внедрению кода JavaScript, описывающего объект.

Flask предоставляет для этого фильтр Jinja: tojson выгружает структуру в строку JSON. и помечает его как безопасный, чтобы Jinja не сбегал автоматически.

<html>
  <head>
    <script>
      var myGeocode = {{ geocode|tojson }};
    </script>
  </head>
  <body>
    <p>Hello World</p>
    <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
  </body>
</html>

Это работает для любой структуры Python, сериализуемой в формате JSON:

python_data = {
    'some_list': [4, 5, 6],
    'nested_dict': {'foo': 7, 'bar': 'a string'}
}
var data = {{ python_data|tojson }};
alert('Data: ' + data.some_list[1] + ' ' + data.nested_dict.foo + 
      ' ' + data.nested_dict.bar);
person ford    schedule 14.04.2014
comment
Попробуйте это в следующий раз, когда в консоли javascript появится Uncaught SyntaxError: Unexpected token &. - person scharfmn; 06.01.2015
comment
Я считаю этот ответ более основательным и логичным, чем принятый. - person Konrad; 22.03.2018
comment
Я получил ошибку при запуске кода: TypeError: Object of type Undefined is not JSON serializable - person jbuddy_13; 25.05.2020

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

Укажите атрибут данных следующим образом:

<div id="mydiv" data-geocode='{{ geocode|tojson }}'>...</div>

Затем получите доступ к нему в статическом файле JavaScript, например:

// Raw JavaScript
var geocode = JSON.parse(document.getElementById("mydiv").dataset.geocode);

// jQuery
var geocode = JSON.parse($("#mydiv").data("geocode"));
person mcote    schedule 30.06.2017

В качестве альтернативы вы можете добавить конечную точку для возврата вашей переменной:

@app.route("/api/geocode")
def geo_code():
    return jsonify(geocode)

Затем выполните XHR, чтобы получить его:

fetch('/api/geocode')
  .then((res)=>{ console.log(res) })
person Vinnie James    schedule 15.05.2017
comment
Да, вы могли, и в некоторых архитектурах (особенно в SPA) это правильный способ делать что-то, но имейте в виду, что у этого есть несколько недостатков по сравнению с записью данных на страницу, когда вы обслуживаете его: 1. он медленнее, 2. он требует немного больше кода, даже если он работает небрежно, и 3. он вводит как минимум два дополнительных состояния внешнего интерфейса, которые вам, вероятно, нужно будет аккуратно обработать в своем пользовательском интерфейсе (а именно состояние, в котором запрос XHR все еще находится в стадии разработки, и тот, где он полностью завершился неудачно), что требует кучу дополнительного кода JavaScript и вводит дополнительный источник потенциальных ошибок. - person Mark Amery; 27.12.2018

Еще одно альтернативное решение для тех, кто хочет передавать переменные в скрипт, полученный с помощью flask, мне удалось заставить это работать, только определив переменные снаружи, а затем вызвав скрипт следующим образом:

    <script>
    var myfileuri = "/static/my_csv.csv"
    var mytableid = 'mytable';
    </script>
    <script type="text/javascript" src="/static/test123.js"></script>

Если я введу переменные jinja в test123.js, это не сработает, и вы получите сообщение об ошибке.

person tsando    schedule 10.02.2017
comment
-1; этот ответ не имеет смысла. Я думаю (исходя из формулировки сценарий, полученный с использованием flask и вашего очевидного ожидания, что вы сможете использовать переменные шаблона в /static/test123.js), что вы неправильно понимаете, как работают <script>s с srcs. Это не функция Flask. браузер, когда он разбирает такой скрипт, делает отдельный HTTP-запрос для получения скрипта. Содержимое скрипта не запекается в шаблоне Flask; действительно, Flask, скорее всего, завершил отправку шаблонного HTML в браузер к тому моменту, когда браузер даже запросит скрипт. - person Mark Amery; 27.12.2018

Рабочие ответы уже даны, но я хочу добавить проверку, которая действует как отказоустойчивая в случае, если переменная flask недоступна. Когда вы используете:

var myVariable = {{ flaskvar | tojson }};

если есть ошибка, из-за которой переменная не существует, возникающие ошибки могут привести к неожиданным результатам. Чтобы этого избежать:

{% if flaskvar is defined and flaskvar %}
var myVariable = {{ flaskvar | tojson }};
{% endif %}
person Patrick Mutuku    schedule 04.07.2018

<script>
    const geocodeArr = JSON.parse('{{ geocode | tojson }}');
    console.log(geocodeArr);
</script>

Он использует jinja2 для преобразования кортежа геокода в строку json, а затем javascript JSON.parse превращает это в массив javascript.

person nackjicholson    schedule 29.12.2018

Что ж, у меня есть хитрый метод для этой работы. Идея заключается в следующем:

Сделайте некоторые невидимые теги HTML, такие как <label>, <p>, <input> и т. Д., В теле HTML и создайте шаблон в идентификаторе тега, например, используйте индекс списка в идентификаторе тега и значение списка в имени класса тега.

Здесь у меня есть два списка maintenance_next [] и maintenance_block_time [] одинаковой длины. Я хочу передать эти два списка данных в javascript с помощью колбы. Итак, я беру какой-то невидимый тег label и устанавливаю его имя тега как образец индекса списка и устанавливаю его имя класса как значение в index.

{% for i in range(maintenance_next|length): %}
<label id="maintenance_next_{{i}}" name="{{maintenance_next[i]}}" style="display: none;"></label>
<label id="maintenance_block_time_{{i}}" name="{{maintenance_block_time[i]}}" style="display: none;"></label>
{% endfor%}

После этого я получаю данные в javascript, используя простую операцию javascript.

<script>
var total_len = {{ total_len }};

for (var i = 0; i < total_len; i++) {
    var tm1 = document.getElementById("maintenance_next_" + i).getAttribute("name");
    var tm2 = document.getElementById("maintenance_block_time_" + i).getAttribute("name");
    
    //Do what you need to do with tm1 and tm2.
    
    console.log(tm1);
    console.log(tm2);
}
</script>

person Tanmoy Datta    schedule 09.12.2019

Некоторые файлы js поступают из Интернета или библиотеки, они не написаны вами. Код они получают такие переменные:

var queryString = document.location.search.substring(1);
var params = PDFViewerApplication.parseQueryString(queryString);
var file = 'file' in params ? params.file : DEFAULT_URL;

Этот метод делает файлы js неизменными (сохраняет независимость) и правильно передает переменную!

person jayzhen    schedule 22.11.2017