ตัวแปรเทมเพลต Django และ Javascript


219

เมื่อฉันทำให้หน้าโดยใช้แม่แบบ Django renderer {{ myVar }}ผมสามารถผ่านในตัวแปรพจนานุกรมมีค่าต่างๆที่จะจัดการกับพวกเขาในหน้าโดยใช้

มีวิธีเข้าถึงตัวแปรเดียวกันใน Javascript (อาจใช้ DOM ฉันไม่รู้ว่า Django ทำให้ตัวแปรสามารถเข้าถึงได้) หรือไม่? ฉันต้องการให้สามารถค้นหารายละเอียดโดยใช้การค้นหา AJAX ตามค่าที่มีอยู่ในตัวแปรที่ส่ง

คำตอบ:


291

{{variable}}แทนโดยตรงในรูปแบบ HTML ทำแหล่งที่มาของมุมมอง; มันไม่ได้เป็น "ตัวแปร" หรืออะไรทำนองนั้น มันเป็นเพียงข้อความที่แสดงผล

ต้องบอกว่าคุณสามารถแทนที่ประเภทนี้ลงใน JavaScript ของคุณ

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

สิ่งนี้จะให้จาวาสคริปต์ "ไดนามิก" ของคุณ


29
โปรดทราบว่าตามโซลูชันนี้มีความเสี่ยงต่อการถูกโจมตีด้วยการฉีด
Casebash

13
@Casebash: สำหรับโอกาสดังกล่าวescapejsมีตัวกรอง:escapejs('<>') -> u'\\u003C\\u003E'
Tomasz Zieliński

24
เพียงเพิ่มเพื่อการอ้างอิงนี้: หาก "someDjangoVariable" เกิดขึ้นเป็น JSON โปรดใช้ {{someDjangoVariable | safe}} เพื่อลบ & quot;
ทำเครื่องหมาย

4
คำตอบนี้ใช้ได้กับตัวแปรอย่างง่ายเท่านั้นมันไม่ทำงานสำหรับโครงสร้างข้อมูลที่ซับซ้อน ในกรณีนี้ทางออกที่ง่ายที่สุดคือการเพิ่มรหัสฝั่งไคลเอ็นต์เพื่อสำรวจโครงสร้างข้อมูลและสร้างรหัสที่คล้ายกันใน Javascript หากโครงสร้างข้อมูลที่ซับซ้อนอยู่ในรูปแบบ JSON โซลูชันอื่นคือการทำให้เป็นอนุกรมส่งผ่าน JSON ต่อเนื่องไปยังเทมเพลต Django ในรหัสฝั่งเซิร์ฟเวอร์และทำการเลิก JSON ในวัตถุ javascript ในรหัสฝั่งไคลเอ็นต์ หนึ่งคำตอบด้านล่างนี้กล่าวถึงทางเลือกนี้
Alan Evangelista

14
เกิดอะไรขึ้นถ้าจาวาสคริปต์ถูกเขียนในไฟล์อื่น?
the_unknown_spirit

64

ข้อควรระวังตรวจสอบตั๋ว# 17419เพื่อหารือเกี่ยวกับการเพิ่มแท็กที่คล้ายกันลงใน Django core และช่องโหว่ XSS ที่เป็นไปได้ที่แนะนำโดยใช้แท็กแม่แบบนี้กับข้อมูลที่ผู้ใช้สร้างขึ้น ความคิดเห็นจาก amacneil กล่าวถึงข้อกังวลส่วนใหญ่ที่เกิดขึ้นในตั๋ว


ฉันคิดว่าวิธีที่ยืดหยุ่นและสะดวกที่สุดในการทำเช่นนี้คือการกำหนดตัวกรองแม่แบบสำหรับตัวแปรที่คุณต้องการใช้ในรหัส JS นี้จะช่วยให้คุณเพื่อให้แน่ใจว่าข้อมูลของคุณจะหนีออกมาได้อย่างถูกต้องและคุณสามารถใช้กับโครงสร้างข้อมูลที่ซับซ้อนเช่นและdict listนั่นเป็นเหตุผลที่ฉันเขียนคำตอบนี้แม้ว่าจะมีคำตอบที่ยอมรับพร้อมกับ upvotes มากมาย

นี่คือตัวอย่างของตัวกรองเทมเพลต:

// 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 และแปลงเป็น JSON ก่อนเรนเดอร์ Django ใช้สิ่งนี้หรือคัดลอกตัวแปร Django ทั้งหมดไปยัง Javascript เสมอ
Alan Evangelista


1
@ JorgeOrpinel ไม่มันไม่เหมือนกัน safeทำเครื่องหมายเฉพาะค่าว่าปลอดภัยโดยไม่มีการแปลงและหลบหนีที่เหมาะสม
ดูแลระบบ Yaroslav

2
คุณจะแสดงตัวแปรในเทมเพลต django ได้อย่างไร?
kbdev

1
@Sandi กลับมาเมื่อฉันโพสต์มันเป็นเรื่องปกติที่จะมีวิดเจ็ตในไฟล์ JS แยกต่างหากและเริ่มต้นมันในซอร์สโค้ดหน้า ดังนั้นสมมติว่าคุณประกาศfunction myWidget(config) { /* implementation */ }ในไฟล์ JS myWidget({{ pythonConfig | js }})และกว่าที่คุณใช้มันในบางหน้าใช้ แต่คุณไม่สามารถใช้งานได้ในไฟล์ JS (ตามที่คุณสังเกตเห็น) ดังนั้นจึงมีข้อ จำกัด
ดูแล Yaroslav

51

วิธีแก้ปัญหาที่ใช้งานได้สำหรับฉันคือการใช้ฟิลด์อินพุตที่ซ่อนอยู่ในเทมเพลต

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

จากนั้นรับค่าใน javascript ด้วยวิธีนี้

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

3
ระวังให้ดี ขึ้นอยู่กับว่าคุณใช้ตัวแปร / ฟอร์มอย่างไรผู้ใช้สามารถใส่สิ่งที่ต้องการได้
AndyL

3
คุณอาจต้องการตั้งค่าฟิลด์อินพุตให้เป็นแบบอ่านอย่างเดียว (ดูที่ลิงค์นี้w3schools.com/tags/att_input_readonly.asp )
nu everest

หากเป็นสิ่งที่จะไม่เปลี่ยนแปลงฐานข้อมูลหรือจะไม่ถูกส่งไปยังแบบสอบถามฐานข้อมูลสิ่งนี้จะใช้ได้ @AndyL
James111

3
พวก ... ผู้ใช้สามารถทำสิ่งที่ต้องการได้ เบราว์เซอร์ทำให้ทุกวันนี้ง่ายขึ้นด้วยเครื่องมือตรวจสอบ DOM ที่มีคุณสมบัติครบถ้วนและเครื่องมือดีบั๊ก คุณธรรมของเรื่อง: ทำสิ่งที่คุณตรวจสอบข้อมูลบนเซิร์ฟเวอร์
2867288

1
ใครก็ได้โปรดบอกฉันว่าคุณจะเข้าถึงตัวแปรนั้นในไฟล์ JavaScript ภายนอกได้อย่างไร?
Ahtisham

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 () หรือถ้าคุณต้องการแปลงจากตัวแบบข้อมูลใน App Engine คุณสามารถใช้ encode () จากไลบรารี GQLEncoder


1
โปรดทราบว่า 'simplejson' กลายเป็น 'json' ตั้งแต่ django 1.7 ฉันเชื่อว่า
fiveclubs

4

ฉันกำลังเผชิญกับปัญหา simillar และคำตอบที่ S.Lott แนะนำให้ฉันทำ

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

อย่างไรก็ตามฉันต้องการจะชี้ให้เห็นข้อ จำกัด การใช้งานที่สำคัญที่นี่ หากคุณวางแผนที่จะวางรหัสจาวาสคริปต์ในไฟล์อื่นและรวมไฟล์นั้นไว้ในแม่แบบของคุณ สิ่งนี้ใช้ไม่ได้

สิ่งนี้ใช้ได้เฉพาะเมื่อคุณแม่แบบหลักและรหัสจาวาสคริปต์อยู่ในไฟล์เดียวกัน ทีม django อาจเป็นไปได้ที่จะแก้ไขข้อ จำกัด นี้


1
เราจะเอาชนะสิ่งนี้ได้อย่างไร?
Csaba Toth

2
เพื่อเอาชนะสิ่งนี้คุณสามารถวางตัวแปร Javascript ทั่วโลกเช่นตัวอย่างที่แสดงก่อนที่คุณจะรวมไฟล์ Javascript แบบคงที่ ไฟล์ Javascript คงที่ของคุณจะสามารถเข้าถึงตัวแปรโกลบอลทั้งหมดได้
Bobort

อย่าใช้สิ่งนี้มีความเสี่ยงต่อการฉีดสคริปต์ (XSS)
pcworld

พวกเขาสามารถข้อมูลที่ถูกต้องในฝั่งเซิร์ฟเวอร์
มูฮัมหมัดฟาซาน

4

ฉันก็ต้องดิ้นรนกับเรื่องนี้เช่นกัน บนพื้นผิวดูเหมือนว่าการแก้ปัญหาข้างต้นควรทำงาน อย่างไรก็ตามสถาปัตยกรรม django ต้องการให้ไฟล์ html แต่ละไฟล์มีตัวแปรที่สร้างการแสดงผลของตัวเอง (นั่นคือ, {{contact}}ถูกเรนเดอร์ไปcontact.html, ในขณะที่{{posts}}ไปที่ eg index.htmlและอื่น ๆ ) บนมืออื่น ๆ , <script>แท็กปรากฏหลังจากที่{%endblock%}ในbase.htmlที่contact.htmlและindex.htmlสืบทอด ซึ่งโดยทั่วไปหมายถึงการแก้ปัญหาใด ๆ รวมถึง

<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 ที่สร้างขึ้นแบบไดนามิกในแบบฟอร์ม) และใช้งานได้ (แต่สำหรับแถว formsetเพียงแถวเดียว ) สิ่งที่ฉันไม่สามารถไปถึงได้คือการจับค่าจากแถวทั้งหมดของเขตข้อมูล formsetซึ่งจะถูกเติมด้วยตนเอง (เช่นในตาราง html ที่ใช้สำหรับการวนซ้ำ ) ในขณะที่คุณจะเห็นภาพตัวแปรของแถว formset ล่าสุดเท่านั้นที่ถูกส่งผ่านไปยังตัวแปรและค่าก่อนหน้านั้นจะถูกเขียนทับด้วยค่าหลังในขณะที่ for วนไปเรื่อยผ่านแถว formset มีวิธีรอบนี้หรือไม่?
12379095

3

สำหรับวัตถุ JavaScript ที่จัดเก็บในฟิลด์ Django เป็นข้อความซึ่งจำเป็นต้องกลายเป็นวัตถุ 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

ส่วนที่ประกาศตัวแปรทั่วโลกมีประโยชน์จริง ๆ
ราหุล

หากคุณแสดงข้อมูลเป็นตัวแปร js จากแม่แบบด้านบนคุณต้องแสดงผลในหน้าเดียวกัน (ทำให้เป็นแบบโกลบอล) และโค้ดอื่น ๆ จะไปที่ไฟล์ js แยกกัน ฝั่งเซิร์ฟเวอร์จะต้องใช้ข้อมูลที่ถูกต้องเพราะผู้ใช้สามารถเปลี่ยนจากเบราว์เซอร์
Muhammad Faizan Fareed

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 หน้าโค้ดจาวาสคริปต์ที่คุณต้องการตัวอย่างเช่น

แบบ

{{ prueba|safe  }}

และด้านล่างคือรหัสที่เหลือของคุณโปรดจำไว้ว่าตัวแปรที่ใช้ในตัวอย่างคือ data2

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

ด้วยวิธีนี้คุณจะรักษาประเภทของข้อมูลซึ่งในกรณีนี้คือข้อตกลง


ใช้สิ่งนี้เฉพาะในกรณีที่คุณแน่ใจว่าaaaจะมีตัวเลขเท่านั้นมิฉะนั้น XSS (การฉีดสคริปต์) เป็นไปได้
pcworld

0

มีสองสิ่งที่เหมาะกับฉันใน Javascript:

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