web-dev-qa-db-ja.com

PythonからJinja2によってJavaScriptにリストを渡す方法

Python変数があるとします:

_list_of_items = ['1','2','3','4','5']
_

hTMLをレンダリングしてJinjaに渡します。また、somefunction(variable)というJavaScriptの関数もあります。 _list_of_items_の各項目を渡そうとしています。私はこのようなものを試しました:

_{% for item in list_of_items %}
<span onclick="somefunction({{item}})">{{item}}</span><br>
{% endfor %}
_

リストをPythonからJavaScriptに渡すことはできますか、またはリスト内の各項目をループで1つずつ渡す必要がありますか?これを行うにはどうすればよいですか?

55
user1843766

一部のコンテキストデータをjavascriptコードに渡すには、javascript(つまりJSON)によって「理解」されるようにシリアル化する必要があります。また、データがhtmlescapedされないように、safe Jinjaフィルターを使用して安全とマークする必要があります。

これを実現するには、次のようなことを行います。

景色

import json

@app.route('/')
def my_view():
    data = [1, 'foo']
    return render_template('index.html', data=json.dumps(data))

テンプレート

<script type="text/javascript">
    function test_func(data) {
        console.log(data);
    }
    test_func({{ data|safe }})
</script>

編集-正確な答え

したがって、必要なものを正確に実現するには(アイテムのリストをループし、JavaScript関数に渡す)、リスト内のすべてのアイテムを個別にシリアル化する必要があります。コードは次のようになります。

景色

import json

@app.route('/')
def my_view():
    data = [1, "foo"]
    return render_template('index.html', data=map(json.dumps, data))

テンプレート

{% for item in data %}
    <span onclick=someFunction({{ item|safe }});>{{ item }}</span>
{% endfor %}

編集2

私の例では、Flaskを使用しています。どのフレームワークを使用しているかわかりませんが、アイデアはわかっています。使用するフレームワークに合わせて作成する必要があります。

編集3(セキュリティ警告)

ユーザー提供のデータでは絶対にこれを行わないでください。信頼できるデータでのみこれを行ってください!

そうでなければ、アプリケーションをXSSの脆弱性にさらすことになります!

87
mdeous

Flaskを使用して同様の問題が発生しましたが、JSONに頼る必要はありませんでした。 render_template('show_entries.html', letters=letters)を含むリスト_letters = ['a','b','c']_を渡し、設定しました

_var letters = {{ letters|safe }}
_

私のjavascriptコードで。 Jinja2は_{{ letters }}_を_['a','b','c']_に置き換えました。これはjavascriptが文字列の配列として解釈したものです。

26
Godsmith

これを行うには、Jinjaの tojson フィルターを使用します。

構造をJSONにダンプして、二重引用符で囲まれた属性の顕著な例外を除き、HTMLの任意の場所で<script>タグ[および]で安全に使用できるようにします。

たとえば、Pythonで次のように記述します。

some_template.render(list_of_items=list_of_items)

...または、Flaskエンドポイントのコンテキストで:

return render_template('your_template.html', list_of_items=list_of_items)

次に、テンプレートに次のように記述します。

{% for item in list_of_items %}
<span onclick='somefunction({{item | tojson}})'>{{item}}</span><br>
{% endfor %}

onclick属性はsingle-quotedであることに注意してください。|tojson'文字をエスケープするため、これが必要です。ただし、出力には"文字は含まれません。つまり、単一引用符で囲まれたHTML属性では安全に使用できますが、二重引用符で囲まれた属性では使用できません。

または、HTML属性の代わりにインラインスクリプトでlist_of_itemsを使用するには、次のように記述します。

<script>
const jsArrayOfItems = {{list_of_items | tojson}};
// ... do something with jsArrayOfItems in JavaScript ...
</script>

PythonコードでJSONエンコード変数にjson.dumpsを使用して、結果のJSONテキストをテンプレートに渡すことはできません。これにより、一部の文字列値に対して誤った出力が生成され、これは、Pythonの組み込みjson.dumps<>(安全にエスケープする必要がある)などの文字をエスケープしないためです。 https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements )に記載されているように、テンプレート値をインライン<script>sに入れますまたは、単一引用符(安全に値を単一引用符で囲まれたHTML属性にテンプレート化するにはエスケープする必要があります)。

Flaskを使用している場合、FlaskはJinjaのバージョンを使用する代わりに、カスタム tojson フィルターを挿入します。ただし、上記のすべてが適用されます。 2つのバージョンはほぼ同じように動作します; Flaskの単なる アプリ固有の設定が可能 これはJinjaのバージョンでは利用できません。

1
Mark Amery

選択した答えを合計するために、jinja2とflaskを使用して動作する新しいオプションをテストしています:

@app.route('/')
def my_view():
    data = [1, 2, 3, 4, 5]
    return render_template('index.html', data=data)

テンプレート:

<script>
    console.log( {{ data | tojson }} )
</script>

レンダリングされたテンプレートの出力:

<script>
    console.log( [1, 2, 3, 4] )
</script>

safeを追加することもできますが、{{ data | tojson | safe }}しかし、それはあまりにも機能しています。

0
Sylhare

プロジェクトでjavascriptファイルを簡単に操作できるようにするjavascript指向のアプローチを提案できます。

Jinjaテンプレートファイルでjavascriptセクションを作成し、使用するすべての変数をjavascriptファイルでウィンドウオブジェクトに配置します。

Start.html

...
{% block scripts %}
<script type="text/javascript">
window.appConfig = {
    debug: {% if env == 'development' %}true{% else %}false{% endif %},
    facebook_app_id: {{ facebook_app_id }},
    accountkit_api_version: '{{ accountkit_api_version }}',
    csrf_token: '{{ csrf_token }}'
}
</script>
<script type="text/javascript" src="{{ url_for('static', filename='app.js') }}"></script>
{% endblock %}

Jinjaは値を置換し、appConfigオブジェクトは他のスクリプトファイルから到達可能になります。

App.js

var AccountKit_OnInteractive = function(){
    AccountKit.init({
        appId: appConfig.facebook_app_id,
        debug: appConfig.debug,
        state: appConfig.csrf_token,
        version: appConfig.accountkit_api_version
    })
}

管理しやすくseoに優しいこの方法で、htmlドキュメントからjavascriptコードを分離しました。

0
muratgozel