Variables de plantilla de Django y Javascript

219

Cuando renderizo una página usando el renderizador de plantillas de Django, puedo pasar una variable de diccionario que contiene varios valores para manipularlos en la página {{ myVar }}.

¿Hay alguna manera de acceder a la misma variable en Javascript (quizás usando DOM, no sé cómo Django hace que las variables sean accesibles)? Quiero poder buscar detalles usando una búsqueda AJAX basada en los valores contenidos en las variables pasadas.

AlMcLean
fuente

Respuestas:

291

El {{variable}}se sustituye directamente en el HTML. Hacer una fuente de vista; no es una "variable" ni nada parecido. Es solo texto rendido.

Dicho esto, puede poner este tipo de sustitución en su JavaScript.

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

Esto le proporciona javascript "dinámico".

S.Lott
fuente
29
Sin embargo , tenga en cuenta que según esta solución , esto es vulnerable a los ataques de inyección
Casebash el
13
@Casebash: Para tales ocasiones escapejsexiste filtro:escapejs('<>') -> u'\\u003C\\u003E'
Tomasz Zieliński
24
Solo para agregar a esto como referencia: si "someDjangoVariable" resulta ser JSON, asegúrese de usar {{someDjangoVariable | safe}} para eliminar el & quot;
Marcar
44
Esta respuesta solo funciona para una variable simple, no funciona para una estructura de datos compleja. En este caso, la solución más simple es agregar código del lado del cliente para atravesar la estructura de datos y construir uno similar en Javascript. Si la estructura de datos compleja está en formato JSON, otra solución es serializarla, pasar un JSON serializado a la plantilla de Django en el código del lado del servidor y deserializar el JSON en un objeto javascript en el código del lado del cliente. Una respuesta a continuación menciona esta alternativa.
Alan Evangelista
14
¿Qué pasa si el JavaScript está escrito en un archivo diferente?
the_unknown_spirit
64

PRECAUCIÓN Verifique el boleto # 17419 para obtener información sobre cómo agregar etiquetas similares al núcleo de Django y posibles vulnerabilidades XSS introducidas mediante el uso de esta etiqueta de plantilla con datos generados por el usuario. El comentario de amacneil analiza la mayoría de las preocupaciones planteadas en el boleto.


Creo que la forma más flexible y práctica de hacerlo es definir un filtro de plantilla para las variables que desea usar en el código JS. Esto le permite asegurarse de que sus datos se escapen correctamente y puede usarlos con estructuras de datos complejas, como dicty list. Es por eso que escribo esta respuesta a pesar de que hay una respuesta aceptada con muchos votos a favor.

Aquí hay un ejemplo de filtro de plantilla:

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

Esta plantilla filtra convierte la variable a una cadena JSON. Puedes usarlo así:

// myapp/templates/example.html

{% load js %}

<script type="text/javascript">
    var someVar = {{ some_var | js }};
</script>
Administrador Yaroslav
fuente
3
Eso es bueno porque permite copiar solo algunas variables de entrada de plantilla de Django a Javascript y el código del lado del servidor no necesita saber qué estructuras de datos debe usar Javascript y, por lo tanto, convertir a JSON antes de representar la plantilla de Django. Utilice esto o siempre copie todas las variables de Django a Javascript.
Alan Evangelista
Pero tenga en cuenta: stackoverflow.com/questions/23752156/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
@JorgeOrpinel No, no es lo mismo. safesolo marca el valor como seguro, sin una conversión y escape adecuados.
Yaroslav Admin
2
¿Cómo se muestra la variable en la plantilla django?
kbdev
1
@Sandi cuando lo publiqué, era común tener un widget en un archivo JS separado e inicializarlo en el código fuente de la página. Entonces, digamos que declaras function myWidget(config) { /* implementation */ }en el archivo JS y luego lo usas en algunas páginas myWidget({{ pythonConfig | js }}). Pero no puede usarlo en archivos JS (como notó), por lo que tiene sus limitaciones.
Yaroslav Admin
51

Una solución que funcionó para mí es usar el campo de entrada oculto en la plantilla

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

Luego obteniendo el valor en javascript de esta manera,

var myVar = document.getElementById("myVar").value;
bosco
fuente
3
Pero ten cuidado. dependiendo de cómo use la variable / forma, el usuario puede poner lo que quiera.
AndyL
3
también puede configurar su campo de entrada en solo lectura (vea este enlace w3schools.com/tags/att_input_readonly.asp )
nu everest
Si es algo que no alterará una base de datos o no se enviará a una consulta de base de datos, estaría bien. @AndyL
James111
3
Chicos ... los usuarios pueden hacer lo que quieran de todos modos. Los navegadores lo hacen tan fácil hoy en día con un inspector DOM completo y herramientas de depuración. Moraleja de la historia: haga TODAS sus validaciones de datos en el servidor .
user2867288
1
¿Alguien puede decirme cómo accedería a esa variable en un archivo JavaScript externo?
Ahtisham
27

A partir de Django 2.1, una nueva construida en etiqueta de plantilla se ha introducido específicamente para este caso de uso: json_script.

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

La nueva etiqueta serializará de forma segura los valores de plantilla y protege contra XSS.

Extracto de Django docs:

Produce de forma segura un objeto Python como JSON, envuelto en una etiqueta, listo para usar con JavaScript.

Jon Sakas
fuente
10

Esto es lo que estoy haciendo muy fácilmente: modifiqué mi archivo base.html para mi plantilla y lo puse en la parte inferior:

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

luego, cuando quiero usar una variable en los archivos javascript, creo un diccionario DJdata y lo agrego al contexto mediante un json: context['DJdata'] = json.dumps(DJdata)

¡Espero eso ayude!

Insomniak
fuente
No use esto, es vulnerable a la inyección de script (XSS).
pcworld
8

Para un diccionario, primero es mejor codificar a JSON. Puede usar simplejson.dumps () o si desea convertir de un modelo de datos en App Engine, puede usar encode () de la biblioteca GQLEncoder.

jamtoday
fuente
1
Tenga en cuenta que 'simplejson' se convirtió en 'json' a partir de django 1.7, creo.
fiveclubs
4

Estaba enfrentando un problema similar y la respuesta sugerida por S.Lott funcionó para mí.

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

Sin embargo, me gustaría señalar aquí una importante limitación de implementación . Si planea poner su código javascript en un archivo diferente e incluir ese archivo en su plantilla. Esto no funcionara.

Esto funciona solo cuando la plantilla principal y el código de JavaScript están en el mismo archivo. Probablemente el equipo de django pueda abordar esta limitación.

Conquistador
fuente
1
¿Cómo podemos superar esto?
Csaba Toth
2
Para superar esto, puede colocar variables Javascript globales como el ejemplo que se muestra justo antes de incluir el archivo Javascript estático. Su archivo Javascript estático tendrá acceso a todas las variables globales de esa manera.
Bobort
No use esto, es vulnerable a la inyección de script (XSS).
pcworld
Pueden validar datos en el lado del servidor.
Muhammad Faizan Fareed
4

He estado luchando con esto también. En la superficie, parece que las soluciones anteriores deberían funcionar. Sin embargo, la arquitectura django requiere que cada archivo html tenga sus propias variables renderizadas (es decir, {{contact}}se procesa en contact.html, mientras que {{posts}}va a, por ejemplo, index.htmletc.). Por otro lado, <script>las etiquetas aparecen después de la {%endblock%}en base.htmlde la cual contact.htmly index.htmlheredar. Esto básicamente significa que cualquier solución que incluya

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

está destinado a fallar, porque la variable y el script no pueden coexistir en el mismo archivo.

La solución simple que finalmente se me ocurrió, y funcionó para mí, fue simplemente envolver la variable con una etiqueta con id y luego referirme a ella en el archivo js, ​​así:

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

y entonces:

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

y basta con incluir <script src="static/js/somecode.js"></script>en base.htmlla forma habitual. Por supuesto, esto solo se trata de obtener el contenido. Con respecto a la seguridad, solo siga las otras respuestas.

Shoval Sadde
fuente
Probablemente desee usar en .textContentlugar de .innerHTML, porque de lo contrario, las entidades que se codifican en HTML también formarán parte de la variable JS. Pero incluso entonces podría no reproducirse 1: 1 (no estoy seguro).
pcworld
Una aclaracion . Utilizo una forma similar para capturar valores de campo en variables (usando ID creados dinámicamente en el formulario), y está funcionando (pero solo para UNA fila de conjunto de formularios ). A lo que no puedo acceder es a capturar valores de todas las filas de campos de formularios , que se rellenan manualmente (es decir, en una tabla html usando for loop ). Como visualizará, las variables de solo la última fila del conjunto de formularios se pasan a las variables, y los valores anteriores se sobrescriben con los últimos valores a medida que el bucle for avanza por las filas del conjunto de formularios. ¿Hay alguna forma de evitar esto?
user12379095
3

Para un objeto JavaScript almacenado en un campo de Django como texto, que debe volver a convertirse en un objeto JavaScript insertado dinámicamente en el script de la página, debe usar ambos escapejsy JSON.parse():

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

Django escapejsmaneja la cita correctamente y JSON.parse()convierte la cadena de nuevo en un objeto JS.

shacker
fuente
2

Tenga en cuenta que si desea pasar una variable a un script .js externo, debe preceder su etiqueta de script con otra etiqueta de script que declare una variable global.

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

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

data se define en la vista como de costumbre en el get_context_data

def get_context_data(self, *args, **kwargs):
    context['myVar'] = True
    return context
Daniel Kislyuk
fuente
La parte para declarar una variable globalmente fue realmente útil.
Rahul
si procesa datos en variables js desde plantillas como las anteriores, debe hacerlo en la misma página (hacerlas globales) y otro código va a un archivo js separado. En el lado del servidor debe tener datos válidos porque el usuario puede cambiar desde el navegador.
Muhammad Faizan Fareed
1

Lo uso de esta manera en Django 2.1 y trabajo para mí y de esta manera es seguro (referencia) :

Lado Django:

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

Lado HTML:

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

puede ensamblar todo el script donde su variable de matriz se declara en una cadena, de la siguiente manera,

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

eso generará una cadena de la siguiente manera

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

después de tener esta cadena, debe enviarla a la plantilla

views.py

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

en la plantilla la recibe e interpreta de manera literaria como código htm, justo antes del código javascript donde lo necesita, por ejemplo

modelo

{{ prueba|safe  }}

y debajo de eso está el resto de su código, tenga en cuenta que la variable a usar en el ejemplo es data2

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

de esa manera mantendrá el tipo de datos, que en este caso es un acuerdo

Daniel Muñoz
fuente
Use esto solo si está seguro de que aaasolo contendrá números, de lo contrario es posible XSS (inyección de script).
pcworld
0

Hay dos cosas que me funcionaron dentro de Javascript:

'{{context_variable|escapejs }}'

y otros: en views.py

from json import dumps as jdumps

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

y en el html:

{{ message|safe }}
MbeforeL
fuente