Variabel Template Django dan Javascript

219

Ketika saya merender halaman menggunakan renderer template Django, saya bisa memberikan variabel kamus yang berisi berbagai nilai untuk memanipulasi mereka di halaman menggunakan {{ myVar }}.

Apakah ada cara untuk mengakses variabel yang sama dalam Javascript (mungkin menggunakan DOM, saya tidak tahu bagaimana Django membuat variabel dapat diakses)? Saya ingin dapat mencari rincian menggunakan pencarian AJAX berdasarkan nilai-nilai yang terkandung dalam variabel yang diteruskan.

AlMcLean
sumber

Jawaban:

291

Ini {{variable}}diganti secara langsung ke dalam HTML. Apakah sumber tampilan; itu bukan "variabel" atau semacamnya. Itu hanya teks yang dirender.

Karena itu, Anda dapat memasukkan subtitusi ini ke dalam JavaScript Anda.

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

Ini memberi Anda javascript "dinamis".

S.Lott
sumber
29
Perhatikan bahwa menurut solusi ini , ini rentan terhadap serangan injeksi
Casebash
13
@Casebash: Untuk acara-acara seperti itu escapejsada filter:escapejs('<>') -> u'\\u003C\\u003E'
Tomasz Zieliński
24
Untuk menambahkan referensi ini: jika "someDjangoVariable" adalah JSON, pastikan untuk menggunakan {{someDjangoVariable | safe}} untuk menghapus & quot;
Markus
4
Jawaban ini hanya berfungsi untuk variabel sederhana, itu tidak berfungsi untuk struktur data yang kompleks. Dalam hal ini, solusi paling sederhana adalah menambahkan kode sisi klien untuk melintasi struktur data dan membangun yang serupa di Javascript. Jika struktur data yang kompleks adalah dalam format JSON, solusi lain adalah membuat serialisasi, meneruskan JSON berseri ke template Django dalam kode sisi-server dan deserialisasi JSON dalam objek javascript dalam kode sisi-klien. Satu jawaban di bawah menyebutkan alternatif ini.
Alan Evangelista
14
bagaimana jika javascript ditulis dalam file yang berbeda?
the_unknown_spirit
64

HATI-HATI Periksa tiket # 17419 untuk diskusi tentang menambahkan tag serupa ke inti Django dan kemungkinan kerentanan XSS yang diperkenalkan dengan menggunakan tag templat ini dengan data yang dibuat pengguna. Komentar dari amacneil membahas sebagian besar kekhawatiran yang muncul dalam tiket.


Saya pikir cara paling fleksibel dan praktis untuk melakukan ini adalah dengan mendefinisikan filter template untuk variabel yang ingin Anda gunakan dalam kode JS. Ini memungkinkan Anda untuk memastikan, bahwa data Anda lolos dengan benar dan Anda dapat menggunakannya dengan struktur data yang kompleks, seperti dictdan list. Itu sebabnya saya menulis jawaban ini meskipun ada jawaban yang diterima dengan banyak upvotes.

Berikut ini contoh filter templat:

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

Filter template ini mengonversi variabel menjadi string JSON. Anda dapat menggunakannya seperti ini:

// myapp/templates/example.html

{% load js %}

<script type="text/javascript">
    var someVar = {{ some_var | js }};
</script>
Yaroslav Admin
sumber
3
Itu bagus karena memungkinkan menyalin hanya beberapa variabel input template Django ke Javascript dan kode sisi server tidak perlu tahu struktur data mana yang harus digunakan oleh Javascript dan karenanya dikonversi ke JSON sebelum merender template Django. Baik gunakan ini atau selalu salin semua variabel Django ke Javascript.
Alan Evangelista
Tetapi perhatikan: stackoverflow.com/questions/23752156/…
Ciro Santilli 郝海东 冠状 病 六四 六四 事件 法轮功
1
@JorgeOrpinel Tidak, tidak sama. safehanya menandai nilai sebagai aman, tanpa konversi dan melarikan diri yang tepat.
Yaroslav Admin
2
Bagaimana Anda kemudian menampilkan variabel dalam template Django?
kbdev
1
@Sandi kembali ketika saya mempostingnya, itu biasa untuk memiliki widget di file JS terpisah dan menginisialisasi dalam kode sumber halaman. Jadi katakanlah Anda mendeklarasikan function myWidget(config) { /* implementation */ }dalam file JS dan daripada Anda menggunakannya pada beberapa halaman menggunakan myWidget({{ pythonConfig | js }}). Tetapi Anda tidak dapat menggunakannya dalam file JS (seperti yang Anda perhatikan), sehingga memiliki keterbatasan.
Yaroslav Admin
51

Solusi yang berfungsi untuk saya adalah menggunakan bidang input tersembunyi di template

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

Kemudian dapatkan nilai dalam javascript dengan cara ini,

var myVar = document.getElementById("myVar").value;
bosco-
sumber
3
berhati-hatilah. tergantung pada bagaimana Anda menggunakan variabel / formulir, pengguna dapat memasukkan apa pun yang mereka inginkan.
AndyL
3
Anda mungkin juga ingin mengatur bidang input Anda menjadi hanya baca (lihat tautan ini w3schools.com/tags/att_input_readonly.asp )
nu everest
Jika itu adalah sesuatu yang tidak akan mengubah database atau tidak akan dikirim ke permintaan database, ini akan baik-baik saja. @AndyL
James111
3
Guys ... pengguna dapat melakukan apa yang mereka inginkan. Browser membuatnya sangat mudah saat ini dengan inspektur DOM berfitur lengkap dan alat debugging. Moral dari cerita: lakukan SEMUA validasi data Anda di server .
user2867288
1
Dapatkah ada yang memberi tahu saya bagaimana Anda mengakses variabel itu dalam file JavaScript eksternal?
Ahtisham
27

Pada Django 2.1, baru dibangun di tag template telah diperkenalkan secara khusus untuk kasus penggunaan ini: json_script.

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

Tag baru akan membuat serialisasi nilai template dengan aman dan melindungi terhadap XSS.

Kutipan dokumen Django:

Mengeluarkan objek Python dengan aman sebagai JSON, dibungkus dengan tag, siap digunakan dengan JavaScript.

Jon Sakas
sumber
10

Inilah yang saya lakukan dengan sangat mudah: Saya memodifikasi file base.html untuk template saya dan meletakkannya di bagian bawah:

{% if DJdata %}
    <script type="text/javascript">
        (function () {window.DJdata = {{DJdata|safe}};})();
    </script>
{% endif %}

kemudian ketika saya ingin menggunakan variabel dalam file javascript, saya membuat kamus DJdata dan saya menambahkannya ke konteks oleh json: context['DJdata'] = json.dumps(DJdata)

Semoga ini bisa membantu!

Insomniak
sumber
Jangan gunakan ini, ini rentan terhadap injeksi skrip (XSS).
pcworld
8

Untuk kamus, Anda sebaiknya melakukan enkode ke JSON terlebih dahulu. Anda bisa menggunakan simplejson.dumps () atau jika Anda ingin mengonversi dari model data di App Engine, Anda bisa menggunakan encode () dari pustaka GQLEncoder.

jamtoday
sumber
1
Perhatikan bahwa 'simplejson' menjadi 'json' pada Django 1.7, saya percaya.
fiveclubs
4

Saya menghadapi masalah yang sama dan jawaban yang disarankan oleh S.Lott bekerja untuk saya.

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

Namun saya ingin menunjukkan batasan implementasi utama di sini. Jika Anda berencana untuk meletakkan kode javascript Anda di file yang berbeda dan memasukkan file itu ke dalam template Anda. Ini tidak akan berhasil.

Ini bekerja hanya ketika Anda template utama dan kode javascript di file yang sama. Mungkin tim Django dapat mengatasi keterbatasan ini.

Conquistador
sumber
1
Bagaimana kita bisa mengatasi ini?
Csaba Toth
2
Untuk mengatasinya, Anda dapat menempatkan variabel Javascript global seperti contoh yang ditunjukkan tepat sebelum Anda menyertakan file Javascript statis. File Javascript statis Anda akan memiliki akses ke semua variabel global dengan cara itu.
Bobort
Jangan gunakan ini, ini rentan terhadap injeksi skrip (XSS).
pcworld
Mereka dapat memvalidasi data di sisi server.
Muhammad Faizan Fareed
4

Saya telah berjuang dengan ini juga. Di permukaan, tampaknya solusi di atas harus bekerja. Namun, arsitektur Django mensyaratkan bahwa setiap file html memiliki variabel yang dirender sendiri (yaitu, {{contact}}diberikan ke contact.html, sementara {{posts}}pergi ke misalnya index.htmldan seterusnya). Di sisi lain, <script>tag muncul setelah {%endblock%}in base.htmldari mana contact.htmldan index.htmlmewarisi. Ini pada dasarnya berarti bahwa semua solusi termasuk

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

pasti gagal, karena variabel dan skrip tidak dapat hidup berdampingan dalam file yang sama.

Solusi sederhana yang akhirnya saya buat, dan bekerja untuk saya, adalah dengan hanya membungkus variabel dengan tag dengan id dan kemudian merujuknya dalam file js, seperti:

// index.html
<div id="myvar">{{ myVar }}</div>

lalu:

// somecode.js
var someVar = document.getElementById("myvar").innerHTML;

dan hanya termasuk <script src="static/js/somecode.js"></script>dalam base.htmlseperti biasa. Tentu saja ini hanya tentang mendapatkan konten. Mengenai keamanan, cukup ikuti jawaban lain.

Shoval Sadde
sumber
Anda mungkin ingin menggunakan .textContentsebagai gantinya .innerHTML, karena jika tidak entitas yang mendapatkan HTML-encoded akan menjadi bagian dari variabel JS juga. Tetapi bahkan saat itu mungkin tidak direproduksi 1: 1 (Saya tidak yakin).
pcworld
Klarifikasi . Saya menggunakan cara yang mirip untuk menangkap nilai bidang dalam variabel (menggunakan ID yang dibuat secara dinamis dalam formulir), dan itu berfungsi (tetapi hanya untuk satu baris formset ). Apa yang saya tidak bisa menyiasati, adalah untuk menangkap nilai dari semua baris bidang formset , yang sedang diisi secara manual (yaitu dalam tabel html menggunakan untuk loop ). Saat Anda akan memvisualisasikan variabel hanya baris formset terakhir dilewatkan ke variabel, dan nilai-nilai sebelum yang ditimpa dengan nilai-nilai yang terakhir sebagai untuk loop berlanjut melalui baris formset. Apakah ada cara untuk melakukannya?
user12379095
3

Untuk objek JavaScript yang disimpan dalam bidang Django sebagai teks, yang perlu lagi menjadi objek JavaScript yang disisipkan secara dinamis ke dalam skrip on-page, Anda perlu menggunakan keduanya escapejsdan JSON.parse():

var CropOpts = JSON.parse("{{ profile.last_crop_coords|escapejs }}");

Django escapejsmenangani kutipan dengan benar, dan JSON.parse()mengubah string kembali menjadi objek JS.

shacker
sumber
2

Perhatikan, bahwa jika Anda ingin meneruskan variabel ke skrip .js eksternal maka Anda harus mengawali tag skrip Anda dengan tag skrip lain yang mendeklarasikan variabel global.

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

<script type="text/javascript" src="{% static "scripts/my_script.js" %}"></script>

data didefinisikan dalam tampilan seperti biasa di get_context_data

def get_context_data(self, *args, **kwargs):
    context['myVar'] = True
    return context
Daniel Kislyuk
sumber
Bagian untuk mendeklarasikan variabel secara global sebenarnya sangat membantu.
Rahul
jika Anda merender data menjadi variabel js dari template seperti di atas maka harus merender ke halaman yang sama (menjadikannya global) dan kode lainnya masuk ke file js yang terpisah. Di sisi server harus valid data karena pengguna dapat berubah dari browser.
Muhammad Faizan Fareed
1

Saya menggunakan cara ini di Django 2.1 dan bekerja untuk saya dan cara ini aman (referensi) :

Sisi Django:

def age(request):
    mydata = {'age':12}
    return render(request, 'test.html', context={"mydata_json": json.dumps(mydata)})

Sisi html:

<script type='text/javascript'>
     const  mydata = {{ mydata_json|safe }};
console.log(mydata)
 </script>
Henry
sumber
0

Anda bisa merakit seluruh skrip tempat variabel array Anda dinyatakan dalam sebuah string, seperti berikut,

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

yang akan menghasilkan string sebagai berikut

prueba = "<script>var data2 =['41','56','25','48','72','34','12'];</script>"

setelah memiliki string ini, Anda harus mengirimnya ke templat

views.py

return render(request, 'example.html', {"prueba": prueba})

dalam template Anda menerimanya dan menafsirkannya dengan cara sastra sebagai kode htm, tepat sebelum kode javascript di mana Anda membutuhkannya, misalnya

templat

{{ prueba|safe  }}

dan di bawah itu adalah sisa kode Anda, perlu diingat bahwa variabel yang digunakan dalam contoh adalah data2

<script>
 console.log(data2);
</script>

dengan cara itu Anda akan menyimpan tipe data, yang dalam hal ini adalah pengaturan

Daniel Muñoz
sumber
Gunakan ini hanya jika Anda yakin itu aaahanya akan berisi angka, jika tidak XSS (injeksi skrip) dimungkinkan.
pcworld
0

Ada dua hal yang berhasil bagi saya di dalam Javascript:

'{{context_variable|escapejs }}'

dan lainnya: Di views.py

from json import dumps as jdumps

def func(request):
    context={'message': jdumps('hello there')}
    return render(request,'index.html',context)

dan di html:

{{ message|safe }}
SebelumL
sumber