テンプレートでFlaskからJavaScriptにデータを渡すにはどうすればよいですか?


123

私のアプリは、辞書を返すAPIを呼び出します。このdictからビューのJavaScriptに情報を渡したいのですが。具体的には、JSでGoogle Maps APIを使用しているので、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)

回答:


140

{{ 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>

それを2段階のプロセスと考えてください。まず、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のドキュメントを参照してください。

また、Jinja2の標準フィルターセットtojsonへの追加であるフィルターを指摘しているFordの回答もご覧ください。

2018年11月の編集:tojsonJinja2の標準フィルターセットに含まれるようになりました。


2
非常に義務付けられている、メンシ!それは私の最初の考えでしたが、Flaskのドキュメントでは、JSで{{var}}フォームを使用できることもすぐにはわかりません。それを片付けてくれてありがとう。
MEA

2
@mea:テンプレートエンジンを使用して任意のテキストベースのファイルを生成することもできます。また、動的にTeXファイル(-> PDF)を生成し、電子メールで送信することもできます。非常に用途が
広い

簡単なフォローアップの質問:JSでforループを実行する場合、Python変数内でインデックス変数を使用できますか?{{geocode [i]}}?
MEA

1
それは理にかなっていますが、あなたが投稿したソリューションでは、JS配列の内容を手動でコーディングする必要があるようです。可変長のPythonリストを渡して、JS forループがその長さを反復して、JS配列に追加できるように、もっとプログラムでそれができることを望んでいました。私が自分自身を十分に明確にしていない場合は申し訳ありませんが、JSとWeb開発者にとっては環境にやさしいです。
MEA

1
@RocketPingu別のファイルでデータを渡したい場合は、通常、jsonモジュールを使用してjsonオブジェクトの形式でデータをダンプする方が簡単です
mensi

113

ほとんどすべてのPythonオブジェクトをJavaScriptオブジェクトに取り込む理想的な方法は、JSONを使用することです。JSONはシステム間の転送フォーマットとして優れていますが、JavaScript Object Notationの略であることを忘れることがあります。つまり、JSONをテンプレートに注入することは、オブジェクトを記述するJavaScriptコードを注入することと同じです。

FlaskはこのためのJinjaフィルターを提供しますtojson。Jinjaが自動エスケープしないように、構造をJSON文字列にダンプし、安全であることをマークします。

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

これは、JSONシリアル化可能なすべてのPython構造で機能します。

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);

4
次にUncaught SyntaxError: Unexpected token &JavaScriptコンソールにアクセスするときに、これを試してください。
scharfmn 2015年

8
この答えは、受け入れられた答えよりも堅実で論理的です。
コンラッド

コードの実行中にエラーが発生しました:TypeError: Object of type Undefined is not JSON serializable
jbuddy_13

27

使用するデータ属性を 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"));

11

または、エンドポイントを追加して変数を返すこともできます。

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

次に、XHRを実行してそれを取得します。

fetch('/api/geocode')
  .then((res)=>{ console.log(res) })

はい、あなたの可能性(ESPのSPA)、およびいくつかのアーキテクチャでは、これはあなたがそれを提供する際のページにデータを焼く対これを行うにはいくつかの欠点があることが、物事を行うための適切な方法ですが、心の中でクマ:1.それはさんより遅い、2。だらしなくても少し多くのコードが必要、そして3. UIでクリーンに処理する必要がある可能性が高い追加のフロントエンド状態を2つ導入する(つまり、XHRリクエストがまだ実行中の状態) 、それが完全に失敗した場合)、追加のJavaScriptコードの束を必要とし、潜在的なバグの追加のソースを導入します。
Mark Amery 2018

3

フラスコを使用して供給されるスクリプトに変数を渡したい人のためのもう1つの代替ソリューションですが、外部で変数を定義し、次のようにスクリプトを呼び出すことで、これをうまく機能させることができました。

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

jinja変数を入力しtest123.jsても機能せず、エラーが発生します。


まさに私が探していたもの。
shrishinde 2017

1
-1; この答えは意味がありません。(「フラスコを使用してソースされるスクリプト」という表現と、テンプレート変数をで使用できるというあなたの明白な期待に基づいて/static/test123.js)、<script>sとsrcsの動作方法を誤解していると思います。それらはFlask機能ではありません。ブラウザには、このようなスクリプトを解析し、スクリプトを取得するために、別のHTTP要求を行います。スクリプトのコンテンツは、Flaskによってテンプレートに組み込まれません。実際、Flaskは、ブラウザーがスクリプトを要求するまでに、テンプレート化されたHTMLをブラウザーに送信し終えている可能性があります。
Mark Amery 2018

3

実用的な回答はすでに与えられていますが、フラスコ変数が利用できない場合にフェールセーフとして機能するチェックを追加したいと思います。使用する場合:

var myVariable = {{ flaskvar | tojson }};

変数が存在しない原因となるエラーがある場合、結果として発生するエラーにより予期しない結果が生じる可能性があります。これを回避するには:

{% if flaskvar is defined and flaskvar %}
var myVariable = {{ flaskvar | tojson }};
{% endif %}

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

これはjinja2を使用してジオコードタプルをjson文字列に変換し、javascript JSON.parseはそれをjavascript配列に変換します。


1
あなただけのpythonでJSONをシリアライズしていないのはなぜ
Mojimi

0

まあ、私はこの仕事のためのトリッキーな方法を持っています。アイデアは以下の通りです

<label>, <p>, <input>HTMLボディなどで非表示のHTMLタグをいくつか作成し、タグIDにパターンを作成します。たとえば、タグIDにリストインデックスを使用し、タグクラス名にリスト値を使用します。

ここには、同じ長さの2つのリストmaintenance_next []およびmaintenance_block_time []があります。フラスコを使用して、これら2つのリストのデータをJavaScriptに渡します。だから私はいくつかの目に見えないラベルタグを取り、そのタグ名をリストインデックスのパターンに設定し、そのクラス名をインデックスの値として設定します。

{% 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>


-1

一部のjsファイルはWebまたはライブラリからのもので、自分で作成したものではありません。彼らはこのように変数を取得するコード:

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

このメソッドはjsファイルを変更せず(独立性を維持)、変数を正しく渡します!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.