Variables du modèle Django et Javascript

219

Lorsque je rends une page à l'aide du rendu de modèle Django, je peux passer une variable de dictionnaire contenant diverses valeurs pour les manipuler dans la page à l'aide {{ myVar }}.

Existe-t-il un moyen d'accéder à la même variable en Javascript (peut-être en utilisant le DOM, je ne sais pas comment Django rend les variables accessibles)? Je veux pouvoir rechercher des détails en utilisant une recherche AJAX basée sur les valeurs contenues dans les variables transmises.

AlMcLean
la source

Réponses:

291

Le {{variable}}est directement substitué dans le HTML. Faites une source de vue; ce n'est pas une "variable" ou quelque chose comme ça. C'est juste du texte rendu.

Cela dit, vous pouvez mettre ce type de substitution dans votre JavaScript.

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

Cela vous donne javascript "dynamique".

S.Lott
la source
29
Notez cependant que selon cette solution , cela est vulnérable aux attaques par injection
Casebash
13
@Casebash: Pour de telles occasions, le escapejsfiltre existe:escapejs('<>') -> u'\\u003C\\u003E'
Tomasz Zieliński
24
Juste pour ajouter à cela pour référence: si le "someDjangoVariable" se trouve être JSON, assurez-vous d'utiliser {{someDjangoVariable | safe}} pour supprimer le & quot;
Mark
4
Cette réponse ne fonctionne que pour une variable simple, elle ne fonctionne pas pour une structure de données complexe. Dans ce cas, la solution la plus simple consiste à ajouter du code côté client pour parcourir la structure de données et en créer une similaire en Javascript. Si la structure de données complexe est au format JSON, une autre solution consiste à la sérialiser, à passer un JSON sérialisé au modèle Django en code côté serveur et à désérialiser le JSON dans un objet javascript en code côté client. Une réponse ci-dessous mentionne cette alternative.
Alan Evangelista
14
que faire si le javascript est écrit dans un fichier différent?
the_unknown_spirit
64

ATTENTION Consultez le ticket # 17419 pour une discussion sur l'ajout d'une balise similaire dans le noyau Django et les vulnérabilités XSS possibles introduites en utilisant cette balise de modèle avec des données générées par l'utilisateur. Le commentaire d'Amacneil traite de la plupart des préoccupations soulevées dans le ticket.


Je pense que la façon la plus flexible et pratique de le faire est de définir un filtre de modèle pour les variables que vous souhaitez utiliser dans le code JS. Cela vous permet de vous assurer que vos données sont correctement échappées et que vous pouvez les utiliser avec des structures de données complexes, telles que dictet list. C'est pourquoi j'écris cette réponse malgré qu'il existe une réponse acceptée avec beaucoup de votes positifs.

Voici un exemple de filtre de modèle:

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

Ce modèle filtre convertit la variable en chaîne JSON. Vous pouvez l'utiliser comme ceci:

// myapp/templates/example.html

{% load js %}

<script type="text/javascript">
    var someVar = {{ some_var | js }};
</script>
Yaroslav Admin
la source
3
C'est bien car cela permet de copier uniquement certaines variables d'entrée du modèle Django dans Javascript et le code côté serveur n'a pas besoin de savoir quelles structures de données doivent être utilisées par Javascript et donc converties en JSON avant de rendre le modèle Django. Utilisez-le ou copiez toujours toutes les variables Django dans Javascript.
Alan Evangelista
Mais notez: stackoverflow.com/questions/23752156/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
@JorgeOrpinel Non, ce n'est pas pareil. safemarque uniquement la valeur comme sûre, sans conversion et échappement appropriés.
Yaroslav Admin
2
Comment affichez-vous ensuite la variable dans le modèle django?
kbdev
1
@Sandi en arrière quand je l'ai posté, il était courant d'avoir un widget dans un fichier JS séparé et de l'initialiser dans le code source de la page. Disons donc que vous déclarez function myWidget(config) { /* implementation */ }dans le fichier JS et que vous l'utilisez sur certaines pages en utilisant myWidget({{ pythonConfig | js }}). Mais vous ne pouvez pas l'utiliser dans les fichiers JS (comme vous l'avez remarqué), il a donc ses limites.
Yaroslav Admin
51

Une solution qui a fonctionné pour moi utilise le champ de saisie caché dans le modèle

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

Ensuite, obtenir la valeur en javascript de cette façon,

var myVar = document.getElementById("myVar").value;
bosco-
la source
3
méfiez-vous cependant. selon la façon dont vous utilisez la variable / formulaire, l'utilisateur peut mettre ce qu'il veut.
AndyL
3
vous pouvez également définir votre champ de saisie en lecture seule (voir ce lien w3schools.com/tags/att_input_readonly.asp )
nu everest
Si c'est quelque chose qui ne modifiera pas une base de données ou ne sera pas envoyé à une requête de base de données, ce serait bien. @AndyL
James111
3
Les gars ... les utilisateurs peuvent faire ce qu'ils veulent de toute façon. Les navigateurs facilitent la tâche de nos jours avec un inspecteur DOM complet et des outils de débogage. Morale de l'histoire: faites TOUTES vous la validation des données sur le serveur .
user2867288
1
S'il vous plaît, quelqu'un peut-il me dire comment accéderiez-vous à cette variable dans un fichier JavaScript externe?
Ahtisham
27

Depuis Django 2.1, une nouvelle balise de modèle intégrée a été introduite spécifiquement pour ce cas d'utilisation: json_script .

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

La nouvelle balise sérialisera en toute sécurité les valeurs du modèle et protégera contre XSS.

Extrait de la documentation de Django:

Génère en toute sécurité un objet Python au format JSON, enveloppé dans une balise, prêt à être utilisé avec JavaScript.

Jon Sakas
la source
10

Voici ce que je fais très facilement: j'ai modifié mon fichier base.html pour mon modèle et l'ai mis en bas:

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

puis quand je veux utiliser une variable dans les fichiers javascript, je crée un dictionnaire DJdata et je l'ajoute au contexte par un json: context['DJdata'] = json.dumps(DJdata)

J'espère que ça aide!

Insomniak
la source
Ne l'utilisez pas, il est vulnérable à l'injection de script (XSS).
pcworld
8

Pour un dictionnaire, vous devez d'abord encoder en JSON. Vous pouvez utiliser simplejson.dumps () ou si vous souhaitez convertir à partir d'un modèle de données dans App Engine, vous pouvez utiliser encode () à partir de la bibliothèque GQLEncoder.

jamtoday
la source
1
Notez que 'simplejson' est devenu 'json' à partir de django 1.7, je crois.
fiveclubs
4

J'étais confronté à un problème similaire et la réponse suggérée par S.Lott a fonctionné pour moi.

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

Cependant, je voudrais souligner une limitation de mise en œuvre majeure . Si vous prévoyez de mettre votre code javascript dans un fichier différent et d'inclure ce fichier dans votre modèle. Ça ne marchera pas.

Cela ne fonctionne que lorsque votre modèle principal et le code javascript sont dans le même fichier. L'équipe de Django peut probablement résoudre cette limitation.

Conquistador
la source
1
Comment pouvons-nous surmonter cela?
Csaba Toth
2
Pour surmonter cela, vous pouvez placer des variables Javascript globales comme l'exemple montré juste avant d'inclure le fichier Javascript statique. Votre fichier Javascript statique aura ainsi accès à toutes les variables globales.
Bobort
Ne l'utilisez pas, il est vulnérable à l'injection de script (XSS).
pcworld
Ils peuvent valider les données côté serveur.
Muhammad Faizan Fareed
4

J'ai aussi eu du mal avec ça. En surface, il semble que les solutions ci-dessus devraient fonctionner. Cependant, l'architecture django nécessite que chaque fichier html ait ses propres variables rendues (c'est-à-dire, {{contact}}est rendu à contact.html, tandis que {{posts}}va à eg index.htmlet ainsi de suite). En revanche, les <script>balises apparaissent après le{%endblock%} in base.htmldont contact.htmlet index.htmlhéritent. Cela signifie essentiellement que toute solution, y compris

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

est vouée à l'échec, car la variable et le script ne peuvent pas coexister dans le même fichier.

La solution simple que j'ai finalement trouvée et qui a fonctionné pour moi était de simplement envelopper la variable avec une balise avec id et de la référencer plus tard dans le fichier js, comme ceci:

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

puis:

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

et il suffit d' inclure <script src="static/js/somecode.js"></script>dans base.htmlcomme d' habitude. Bien sûr, il s'agit uniquement d' obtenir le contenu. En ce qui concerne la sécurité, suivez simplement les autres réponses.

Shoval Sadde
la source
Vous voudrez probablement utiliser à la .textContentplace de .innerHTML, car sinon les entités qui sont encodées en HTML feront également partie de la variable JS. Mais même alors, il pourrait ne pas être reproduit 1: 1 (je ne suis pas sûr).
pcworld
Une clarification . J'utilise une manière similaire pour capturer les valeurs de champ dans des variables (en utilisant des ID créés dynamiquement dans le formulaire), et cela fonctionne (mais pour UNE seule ligne de jeu de formulaires ). Ce que je ne suis pas en mesure de contourner, c'est de capturer les valeurs de toutes les lignes de champs formset , qui sont remplies manuellement (c'est-à-dire dans une table html utilisant la boucle for ). Comme vous allez visualiser les variables de seulement la dernière ligne du jeu de formulaires est passée aux variables, et les valeurs précédentes sont écrasées par ces dernières valeurs à mesure que la boucle for progresse dans les lignes du jeu de formulaires. Y a-t-il un moyen de contourner cela?
user12379095
3

Pour un objet JavaScript stocké dans un champ Django sous forme de texte, qui doit redevenir un objet JavaScript inséré dynamiquement dans le script sur la page, vous devez utiliser les deux escapejset JSON.parse():

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

Django escapejsgère correctement les guillemets et JSON.parse()reconvertit la chaîne en un objet JS.

shacker
la source
2

Notez que si vous souhaitez transmettre une variable à un script .js externe, vous devez faire précéder votre balise de script d'une autre balise de script qui déclare une variable globale.

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

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

data est défini dans la vue comme d'habitude dans le get_context_data

def get_context_data(self, *args, **kwargs):
    context['myVar'] = True
    return context
Daniel Kislyuk
la source
La partie pour déclarer une variable globalement était réellement utile.
Rahul
si vous restituez des données dans des variables js à partir de modèles comme ci-dessus, vous devez les rendre dans la même page (les rendre globales) et tout autre code va dans un fichier js séparé. Côté serveur doit valider les données car l'utilisateur peut changer de navigateur.
Muhammad Faizan Fareed
1

J'utilise cette méthode dans Django 2.1 et je travaille pour moi et cette méthode est sécurisée (référence) :

Côté Django:

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

Côté html:

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

vous pouvez assembler le script entier où votre variable de tableau est déclarée dans une chaîne, comme suit,

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

qui générera une chaîne comme suit

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

après avoir cette chaîne, vous devez l'envoyer au modèle

views.py

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

dans le modèle vous le recevez et l'interprétez de façon littéraire comme du code htm, juste avant le code javascript où vous en avez besoin, par exemple

modèle

{{ prueba|safe  }}

et en dessous se trouve le reste de votre code, gardez à l'esprit que la variable à utiliser dans l'exemple est data2

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

de cette façon, vous conserverez le type de données, qui dans ce cas est un arrangement

Daniel Muñoz
la source
N'utilisez ceci que si vous êtes sûr qu'il aaane contiendra que des chiffres, sinon XSS (injection de script) est possible.
pcworld
0

Il y a deux choses qui ont fonctionné pour moi dans Javascript:

'{{context_variable|escapejs }}'

et autres: dans views.py

from json import dumps as jdumps

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

et en html:

{{ message|safe }}
MbeforeL
la source