Django-Vorlagenvariablen und Javascript

219

Wenn ich eine Seite mit dem Django-Vorlagenrenderer rendere, kann ich eine Wörterbuchvariable mit verschiedenen Werten übergeben, um sie auf der Seite mit zu bearbeiten {{ myVar }}.

Gibt es eine Möglichkeit, auf dieselbe Variable in Javascript zuzugreifen (möglicherweise mithilfe des DOM weiß ich nicht, wie Django die Variablen zugänglich macht)? Ich möchte in der Lage sein, Details mithilfe einer AJAX-Suche basierend auf den Werten zu suchen, die in den übergebenen Variablen enthalten sind.

AlMcLean
quelle

Antworten:

291

Das {{variable}}wird direkt in den HTML-Code eingefügt. Machen Sie eine Ansichtsquelle; es ist keine "Variable" oder ähnliches. Es ist nur gerenderter Text.

Allerdings können Sie diese Art der Ersetzung in Ihr JavaScript einfügen.

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

Dies gibt Ihnen "dynamisches" Javascript.

S.Lott
quelle
29
Beachten Sie jedoch, dass dies gemäß dieser Lösung anfällig für Injektionsangriffe ist
Casebash
13
@Casebash: Für solche Gelegenheiten gibt es escapejsFilter:escapejs('<>') -> u'\\u003C\\u003E'
Tomasz Zieliński
24
Nur um dies als Referenz hinzuzufügen: Wenn "someDjangoVariable" zufällig JSON ist, verwenden Sie unbedingt {{someDjangoVariable | safe}}, um das & quot;
Mark
4
Diese Antwort funktioniert nur für eine einfache Variable, nicht für eine komplexe Datenstruktur. In diesem Fall besteht die einfachste Lösung darin, clientseitigen Code hinzuzufügen, um die Datenstruktur zu durchlaufen und einen ähnlichen in Javascript zu erstellen. Wenn die komplexe Datenstruktur im JSON-Format vorliegt, besteht eine andere Lösung darin, sie zu serialisieren, einen serialisierten JSON im serverseitigen Code an die Django-Vorlage zu übergeben und den JSON in einem Javascript-Objekt im clientseitigen Code zu deserialisieren. Eine Antwort unten erwähnt diese Alternative.
Alan Evangelista
14
Was ist, wenn das Javascript in einer anderen Datei geschrieben ist?
the_unknown_spirit
64

VORSICHT Überprüfen Sie Ticket Nr. 17419 auf Erläuterungen zum Hinzufügen eines ähnlichen Tags zum Django-Kern und zu möglichen XSS-Schwachstellen, die durch die Verwendung dieses Vorlagen-Tags mit benutzergenerierten Daten verursacht wurden. In einem Kommentar von amacneil werden die meisten im Ticket angesprochenen Bedenken erörtert.


Ich denke, der flexibelste und praktischste Weg, dies zu tun, besteht darin, einen Vorlagenfilter für Variablen zu definieren, die Sie in JS-Code verwenden möchten. Auf diese Weise können Sie sicherstellen, dass Ihre Daten ordnungsgemäß maskiert werden, und Sie können sie mit komplexen Datenstrukturen wie dictund verwenden list. Deshalb schreibe ich diese Antwort, obwohl es eine akzeptierte Antwort mit vielen positiven Stimmen gibt.

Hier ist ein Beispiel für einen Vorlagenfilter:

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

Diese Vorlagenfilter konvertieren Variablen in JSON-Zeichenfolgen. Sie können es so verwenden:

// myapp/templates/example.html

{% load js %}

<script type="text/javascript">
    var someVar = {{ some_var | js }};
</script>
Jaroslawischer Admin
quelle
3
Das ist schön, weil nur einige Eingabevariablen für Django-Vorlagen in Javascript kopiert werden können und serverseitiger Code nicht wissen muss, welche Datenstrukturen von Javascript verwendet und daher vor dem Rendern der Django-Vorlage in JSON konvertiert werden müssen. Verwenden Sie diese Option oder kopieren Sie immer alle Django-Variablen nach Javascript.
Alan Evangelista
Aber beachten Sie: stackoverflow.com/questions/23752156/…
Ciro Santilli 3 冠状 病 六四 事件 3
1
@JorgeOrpinel Nein, es ist nicht dasselbe. safemarkiert den Wert nur als sicher, ohne ordnungsgemäße Konvertierung und Flucht.
Jaroslawischer
2
Wie zeigt man dann die Variable in der Django-Vorlage an?
Kbdev
1
@ Sandi zurück, als ich es veröffentlichte, war es üblich, ein Widget in einer separaten JS-Datei zu haben und es im Seitenquellcode zu initialisieren. Nehmen wir also an, Sie deklarieren function myWidget(config) { /* implementation */ }in einer JS-Datei und verwenden sie dann auf einigen Seiten mit myWidget({{ pythonConfig | js }}). Sie können es jedoch nicht in JS-Dateien verwenden (wie Sie bemerkt haben), daher hat es seine Grenzen.
Jaroslaw Admin
51

Eine Lösung, die für mich funktioniert hat, ist die Verwendung des versteckten Eingabefelds in der Vorlage

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

Dann erhalten Sie den Wert in Javascript auf diese Weise,

var myVar = document.getElementById("myVar").value;
bosco-
quelle
3
Sei aber vorsichtig. Abhängig davon, wie Sie die Variable / das Formular verwenden, kann der Benutzer eingeben, was er möchte.
AndyL
3
Möglicherweise möchten Sie Ihr Eingabefeld auch auf schreibgeschützt einstellen (siehe diesen Link w3schools.com/tags/att_input_readonly.asp )
nu everest
Wenn es etwas ist, das eine Datenbank nicht verändert oder nicht an eine Datenbankabfrage gesendet wird, ist dies in Ordnung. @ AndyL
James111
3
Leute ... Benutzer können sowieso tun, was sie wollen. Browser machen es heutzutage mit einem voll ausgestatteten DOM-Inspektor und Debugging-Tools so einfach. Moral der Geschichte: Führen Sie ALLE Datenüberprüfungen auf dem Server durch .
user2867288
1
Kann mir bitte jemand sagen, wie Sie in einer externen JavaScript-Datei auf diese Variable zugreifen würden?
Ahtisham
27

Ab Django 2.1 wurde speziell für diesen Anwendungsfall ein neues integriertes Template-Tag eingeführt : json_script.

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

Das neue Tag serialisiert Vorlagenvorlagen sicher und schützt vor XSS.

Django docs Auszug:

Gibt ein Python-Objekt sicher als JSON aus, das in ein Tag eingeschlossen und für die Verwendung mit JavaScript bereit ist.

Jon Sakas
quelle
10

Folgendes mache ich ganz einfach: Ich habe meine base.html-Datei für meine Vorlage geändert und diese unten eingefügt:

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

Wenn ich dann eine Variable in den Javascript-Dateien verwenden möchte, erstelle ich ein DJdata-Wörterbuch und füge es durch einen JSON zum Kontext hinzu: context['DJdata'] = json.dumps(DJdata)

Ich hoffe es hilft!

Insomniak
quelle
Verwenden Sie dies nicht, es ist anfällig für Script Injection (XSS).
PCworld
8

Für ein Wörterbuch codieren Sie am besten zuerst in JSON. Sie können simplejson.dumps () verwenden oder, wenn Sie aus einem Datenmodell in App Engine konvertieren möchten, encode () aus der GQLEncoder-Bibliothek verwenden.

jamtoday
quelle
1
Beachten Sie, dass 'simplejson' ab django 1.7 zu 'json' wurde, glaube ich.
Fiveclubs
4

Ich hatte ein ähnliches Problem und die von S.Lott vorgeschlagene Antwort arbeitete für mich.

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

Ich möchte hier jedoch auf wesentliche Einschränkungen bei der Implementierung hinweisen . Wenn Sie planen, Ihren Javascript-Code in einer anderen Datei abzulegen und diese Datei in Ihre Vorlage aufzunehmen. Das wird nicht funktionieren.

Dies funktioniert nur, wenn sich die Hauptvorlage und der Javascript-Code in derselben Datei befinden. Wahrscheinlich kann das Django-Team diese Einschränkung beheben.

Conquistador
quelle
1
Wie können wir das überwinden?
Csaba Toth
2
Um dies zu überwinden, können Sie globale Javascript-Variablen wie im gezeigten Beispiel platzieren, bevor Sie die statische Javascript-Datei einfügen. Ihre statische Javascript-Datei hat auf diese Weise Zugriff auf alle globalen Variablen.
Bobort
Verwenden Sie dies nicht, es ist anfällig für Script Injection (XSS).
PCworld
Sie können Daten auf der Serverseite validieren.
Muhammad Faizan Fareed
4

Ich habe auch damit zu kämpfen. An der Oberfläche scheint es, dass die obigen Lösungen funktionieren sollten. Die Django-Architektur erfordert jedoch, dass jede HTML-Datei ihre eigenen gerenderten Variablen hat (dh {{contact}}wird gerendert contact.html, während {{posts}}z. B. index.htmlusw.). Auf der anderen Seite erscheinen <script>Tags nach dem {%endblock%}In, base.htmlvon dem contact.htmlund index.htmlerben. Dies bedeutet im Grunde, dass jede Lösung einschließlich

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

ist zum Scheitern verurteilt, da die Variable und das Skript nicht in derselben Datei nebeneinander existieren können.

Die einfache Lösung, die ich schließlich fand und für mich arbeitete, bestand darin, die Variable einfach mit einem Tag mit der ID zu versehen und später in der js-Datei darauf zu verweisen:

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

und dann:

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

und ist nur <script src="static/js/somecode.js"></script>in base.htmlwie gewohnt. Natürlich geht es nur darum , den Inhalt zu bekommen. Befolgen Sie in Bezug auf die Sicherheit einfach die anderen Antworten.

Shoval Sadde
quelle
Sie möchten wahrscheinlich .textContentstattdessen verwenden .innerHTML, da ansonsten Entitäten, die HTML-codiert werden, ebenfalls Teil der JS-Variablen sind. Aber selbst dann wird es möglicherweise nicht 1: 1 reproduziert (ich bin mir nicht sicher).
PCworld
Eine Klarstellung . Ich verwende eine ähnliche Methode zum Erfassen von Feldwerten in Variablen (unter Verwendung von IDs, die dynamisch im Formular erstellt wurden), und es funktioniert (jedoch nur für EINE Formset-Zeile ). Was ich nicht erreichen kann, ist das Erfassen von Werten aus allen Zeilen von Formset-Feldern , die manuell ausgefüllt werden (dh in einer HTML-Tabelle mit der for-Schleife ). Wie Sie sehen werden, werden nur die Variablen der letzten Formset-Zeile an die Variablen übergeben, und die Werte davor werden mit den letzteren Werten überschrieben, während die for-Schleife durch die Formset-Zeilen verläuft. Gibt es einen Weg dahin?
user12379095
3

Für ein JavaScript-Objekt, das in einem Django-Feld als Text gespeichert ist und wieder zu einem JavaScript-Objekt werden muss, das dynamisch in ein On-Page-Skript eingefügt wird, müssen Sie beide escapejsund Folgendes verwenden JSON.parse():

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

Django escapejsbehandelt das Anführungszeichen ordnungsgemäß und JSON.parse()konvertiert die Zeichenfolge zurück in ein JS-Objekt.

Shacker
quelle
2

Beachten Sie, dass Sie, wenn Sie eine Variable an ein externes .js-Skript übergeben möchten, Ihrem Skript-Tag ein anderes Skript-Tag voranstellen müssen, das eine globale Variable deklariert.

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

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

data wird in der Ansicht wie gewohnt in der definiert get_context_data

def get_context_data(self, *args, **kwargs):
    context['myVar'] = True
    return context
Daniel Kislyuk
quelle
Der Teil, um eine Variable global zu deklarieren, war tatsächlich hilfreich.
Rahul
Wenn Sie Daten aus Vorlagen wie oben in js-Variablen rendern, müssen Sie auf derselben Seite rendern (global machen), und anderer Code wird in eine separate js-Datei übertragen. Auf der Serverseite müssen gültige Daten vorliegen, da der Benutzer vom Browser aus wechseln kann.
Muhammad Faizan Fareed
1

Ich benutze diesen Weg in Django 2.1 und arbeite für mich und dieser Weg ist sicher (Referenz) :

Django Seite:

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

HTML-Seite:

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

Sie können das gesamte Skript, in dem Ihre Array-Variable deklariert ist, wie folgt in einer Zeichenfolge zusammenstellen:

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

Dadurch wird eine Zeichenfolge wie folgt generiert

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

Nachdem Sie diese Zeichenfolge erhalten haben, müssen Sie sie an die Vorlage senden

views.py

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

In der Vorlage erhalten Sie es und interpretieren es literarisch als HTML-Code, beispielsweise kurz vor dem Javascript-Code, in dem Sie es benötigen

Vorlage

{{ prueba|safe  }}

und darunter befindet sich der Rest Ihres Codes. Beachten Sie, dass die im Beispiel zu verwendende Variable data2 ist

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

Auf diese Weise behalten Sie die Art der Daten, die in diesem Fall eine Vereinbarung ist

Daniel Muñoz
quelle
Verwenden Sie diese Option nur, wenn Sie sicher sind, dass aaasie nur Zahlen enthält. Andernfalls ist XSS (Script Injection) möglich.
PCworld
0

In Javascript haben zwei Dinge für mich funktioniert:

'{{context_variable|escapejs }}'

und andere: In views.py

from json import dumps as jdumps

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

und im HTML:

{{ message|safe }}
MbeforeL
quelle