Djangoテンプレート変数とJavaScript


219

Djangoテンプレートレンダラーを使用してページをレンダリングする場合、さまざまな値を含むディクショナリ変数を渡して、を使用してページでそれらを操作できます{{ myVar }}

JavaScriptで同じ変数にアクセスする方法はありますか(おそらくDOMを使用していますが、Djangoが変数にアクセスできるようにする方法がわかりません)?渡された変数に含まれる値に基づいて、AJAXルックアップを使用して詳細をルックアップできるようにしたいと思います。

回答:


291

{{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テンプレートに渡して、クライアント側コードのJavaScriptオブジェクトのJSONを逆シリアル化することです。以下の1つの回答は、この代替案について言及しています。
アランエヴァンジェリスタ

14
JavaScriptが別のファイルに記述されている場合はどうなりますか?
the_unknown_spirit

64

注意同様のタグをDjangoコアに追加する方法、およびこのテンプレートタグをユーザー生成データで使用することにより発生する可能性のあるXSS脆弱性については、チケット#17419を確認してください。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で使用する必要のあるデータ構造を知る必要がないため、Djangoテンプレートをレンダリングする前にJSONに変換する必要があるため、便利です。これを使用するか、常にすべてのDjango変数をJavaScriptにコピーしてください。
アランエヴァンジェリスタ


1
@JorgeOrpinelいいえ、同じではありません。safe適切な変換とエスケープを行わずに、値を安全とマークするだけです。
Yaroslav管理者

2
次に、djangoテンプレートで変数をどのように表示しますか?
kbdev 2017年

1
@Sandiが投稿したとき、ウィジェットを別のJSファイルに入れて、ページのソースコードで初期化するのが一般的でした。それではfunction myWidget(config) { /* implementation */ }、JSファイルで宣言し、を使用して一部のページで使用するとしますmyWidget({{ pythonConfig | js }})。ただし、JSファイルでは(気付かれたように)使用できないため、制限があります。
Yaroslav管理者2017

51

私のために働いた解決策は、テンプレートの非表示の入力フィールドを使用することです

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

次に、JavaScriptでこの方法で値を取得します。

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

3
ただし注意が必要です。変数/フォームの使用方法に応じて、ユーザーは必要なものを何でも入れることができます。
AndyL、2012年

3
入力フィールドを読み取り専用に設定することもできます(このリンクw3schools.com/tags/att_input_readonly.aspを参照)
nu everest

データベースを変更しないものや、データベースクエリに送信されないものの場合は問題ありません。@AndyL
James111

3
みんな...ユーザーはとにかくやりたいことができる。ブラウザーは、十分な機能を備えたDOMインスペクターとデバッグツールを使用して、今日を非常に簡単にしています。話の教訓:サーバーですべてのデータ検証を行います
user2867288 2017

1
外部のJavaScriptファイルでその変数にどのようにアクセスしますか?
Ahtisham 2017

27

Django 2.1以降、この使用例のために新しい組み込みテンプレートタグが導入されました:json_script

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

新しいタグはテンプレート値を安全にシリアル化し、XSSから保護します。

Djangoドキュメントの抜粋:

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()を使用できます。AppEngineのデータモデルから変換する場合は、GQLEncoderライブラリのencode()を使用できます。


1
django 1.7から「simplejson」が「json」になったことに注意してください。
5クラブ21/09/15

4

私は類似の問題に直面しており、S.Lottが提案した回答がうまくいきました。

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

ただし、ここで主要な実装上の制限を指摘したいと思います。JavaScriptコードを別のファイルに入れて、そのファイルをテンプレートに含めることを計画している場合。これは機能しません。

これは、メインテンプレートとJavaScriptコードが同じファイルにある場合にのみ機能します。おそらくdjangoチームがこの制限に対処できます。


1
どうすればこれを克服できますか?
Csaba Toth

2
これを克服するために、静的Javascriptファイルをインクルードする直前に示されている例のように、グローバルJavaScript変数を配置できます。静的Javascriptファイルは、このようにしてすべてのグローバル変数にアクセスできます。
Bobort 2017年

これを使用しないでください。スクリプトインジェクション(XSS)に対して脆弱です。
pcworld

サーバー側でデータを有効にできます。
Muhammad Faizan

4

私もこれに苦労しています。表面的には、上記のソリューションが機能するはずです。ただし、djangoアーキテクチャでは、各htmlファイルに独自のレンダリングされた変数(つまり、eg などに移動するときにに{{contact}}レンダリングされる)が必要です。一方、contact.html{{posts}}index.html<script>タグは{%endblock%}base.htmlfrom from contact.htmlおよびindex.htmlinheritの後に表示されます。これは基本的に、

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

変数とスクリプトが同じファイルに共存できないため、失敗するようにバインドされています。

私が最終的に思いついて私のために働いた簡単な解決策は、変数をidのタグで単にラップし、後で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

明確化。(フォームで動的に作成されたIDを使用して)変数のフィールド値をキャプチャする同様の方法を使用して、それが機能しています(ただし、フォームセット行は1つだけです)。私が回避できないのは手動で(forループを使用するhtmlテーブルに)入力されているformsetフィールドのすべての行から値をキャプチャすることです。視覚化すると、最後のフォームセット行のみの変数が変数に渡され、forループがフォームセット行を進むにつれて、その前の値が後者の値で上書きされます。これを回避する方法はありますか?
user12379095

3

Djangoフィールドにテキストとして保存されているJavaScriptオブジェクトは、再びページ上のスクリプトに動的に挿入される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

変数をグローバルに宣言する部分は実際に役立ちました。
Rahul

上記のようなテンプレートからjs変数にデータをレンダリングする場合、同じページにレンダリングし(グローバルにする)、他のコードは別のjsファイルに移動する必要があります。ユーザーはブラウザから変更できるため、サーバー側では有効なデータが必要です。
Muhammad Faizan

1

私はDjango 2.1でこの方法を使用して私のために働いており、この方法は安全です(参照)

Django側:

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の内部では、2つのことがうまくいきました。

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