Variáveis ​​de modelo do Django e Javascript

219

Quando renderizo uma página usando o renderizador de modelos do Django, posso passar uma variável de dicionário contendo vários valores para manipulá-los na página usando {{ myVar }}.

Existe uma maneira de acessar a mesma variável em Javascript (talvez usando o DOM, não sei como o Django torna as variáveis ​​acessíveis)? Quero poder pesquisar detalhes usando uma pesquisa AJAX com base nos valores contidos nas variáveis ​​passadas.

AlMcLean
fonte

Respostas:

291

O {{variable}}é substituído diretamente no HTML. Faça uma fonte de exibição; não é uma "variável" ou algo parecido. É apenas um texto renderizado.

Dito isto, você pode colocar esse tipo de substituição no seu JavaScript.

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

Isso fornece javascript "dinâmico".

S.Lott
fonte
29
Nota no entanto, que de acordo com esta solução , este é vulnerável a ataques de injeção
Casebash
13
@Casebash: Para tais ocasiões, escapejsexiste um filtro:escapejs('<>') -> u'\\u003C\\u003E'
Tomasz Zieliński
24
Apenas para adicionar isso para referência: se o "someDjangoVariable" for JSON, use {{someDjangoVariable | safe}} para remover o & quot;
Mark
4
Essa resposta funciona apenas para uma variável simples, não para uma estrutura de dados complexa. Nesse caso, a solução mais simples é adicionar código do lado do cliente para percorrer a estrutura de dados e criar um semelhante em Javascript. Se a estrutura de dados complexa estiver no formato JSON, outra solução é serializá-la, passar um JSON serializado para o modelo Django no código do lado do servidor e desserializar o JSON em um objeto javascript no código do lado do cliente. Uma resposta abaixo menciona essa alternativa.
Alan Evangelista
14
e se o javascript estiver escrito em um arquivo diferente?
the_unknown_spirit
64

CUIDADO Verifique o ticket nº 17419 para obter uma discussão sobre a adição de uma marca similar no núcleo do Django e possíveis vulnerabilidades XSS introduzidas usando essa marca de modelo com dados gerados pelo usuário. O comentário do amacneil discute a maioria das preocupações levantadas no ticket.


Eu acho que a maneira mais flexível e útil de fazer isso é definir um filtro de modelo para variáveis ​​que você deseja usar no código JS. Isso permite garantir que seus dados sejam escapados corretamente e que você possa usá-los com estruturas de dados complexas, como dicte list. É por isso que escrevo esta resposta, apesar de haver uma resposta aceita com muitos votos positivos.

Aqui está um exemplo de filtro de modelo:

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

Esse modelo de filtros converte variável em string JSON. Você pode usá-lo assim:

// myapp/templates/example.html

{% load js %}

<script type="text/javascript">
    var someVar = {{ some_var | js }};
</script>
Yaroslav Admin
fonte
3
Isso é bom porque permite copiar apenas algumas variáveis ​​de entrada do modelo do Django para Javascript e o código do servidor não precisa saber quais estruturas de dados devem ser usadas pelo Javascript e, portanto, convertidas em JSON antes de renderizar o modelo do Django. Use isso ou sempre copie todas as variáveis ​​do Django para Javascript.
Alan Evangelista
Mas observe: stackoverflow.com/questions/23752156/…
Ciro Santilli escreveu:
1
@JorgeOrpinel Não, não é o mesmo. safeapenas marca o valor como seguro, sem conversão e escape adequados.
Admin Yaroslav
2
Como você exibe a variável no modelo django?
kbdev
1
@ Sandi quando eu postei, era comum ter um widget em um arquivo JS separado e inicializá-lo no código fonte da página. Então, digamos que você declare function myWidget(config) { /* implementation */ }no arquivo JS e depois o use em algumas páginas myWidget({{ pythonConfig | js }}). Mas você não pode usá-lo em arquivos JS (como você notou), por isso tem suas limitações.
Yaroslav Administrador
51

Uma solução que funcionou para mim está usando o campo de entrada oculto no modelo

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

Em seguida, obtendo o valor em javascript dessa maneira,

var myVar = document.getElementById("myVar").value;
bosco-
fonte
3
tenha cuidado. dependendo de como você usa a variável / formulário, o usuário pode colocar o que quiser.
AndyL
3
você também pode definir seu campo de entrada como somente leitura (consulte este link w3schools.com/tags/att_input_readonly.asp )
nu everest
Se algo não alterar um banco de dados ou não for enviado para uma consulta ao banco de dados, tudo bem. @AndyL
James111
3
Gente ... os usuários podem fazer o que quiserem de qualquer maneira. Atualmente, os navegadores facilitam as coisas com um inspetor DOM completo e ferramentas de depuração. Moral da história: faça TODA a validação de dados no servidor .
user2867288
1
Alguém pode me dizer como você acessaria essa variável em um arquivo JavaScript externo?
precisa saber é o seguinte
27

A partir do Django 2.1, uma nova tag de modelo incorporada foi introduzida especificamente para este caso de uso: json_script .

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

A nova tag serializa com segurança os valores do modelo e protege contra o XSS.

Trecho dos documentos do Django:

Produz com segurança um objeto Python como JSON, envolto em uma tag, pronto para uso com JavaScript.

Jon Sakas
fonte
10

Aqui está o que estou fazendo com muita facilidade: modifiquei meu arquivo base.html para o meu modelo e coloquei na parte inferior:

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

então, quando quero usar uma variável nos arquivos javascript, crio um dicionário DJdata e o adiciono ao contexto por um json: context['DJdata'] = json.dumps(DJdata)

Espero que ajude!

Insomniak
fonte
Não use isso, pois é vulnerável à injeção de script (XSS).
PCWorld
8

Para um dicionário, é melhor codificar para JSON primeiro. Você pode usar simplejson.dumps () ou, se desejar converter de um modelo de dados no App Engine, poderá usar encode () da biblioteca GQLEncoder.

jamtoday
fonte
1
Note que 'simplejson' se tornou 'json' a partir do django 1.7, acredito.
fiveclubs 21/09/2015
4

Eu estava enfrentando um problema semelhante e a resposta sugerida por S.Lott funcionou para mim.

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

No entanto, gostaria de destacar aqui as principais limitações de implementação . Se você planeja colocar seu código javascript em um arquivo diferente e incluir esse arquivo no seu modelo. Isso não vai funcionar.

Isso funciona apenas quando o modelo principal e o código javascript estão no mesmo arquivo. Provavelmente a equipe do django pode resolver essa limitação.

Conquistador
fonte
1
Como podemos superar isso?
Csaba Toth
2
Para superar isso, você pode colocar variáveis ​​Javascript globais como o exemplo mostrado antes de incluir o arquivo Javascript estático. Seu arquivo Javascript estático terá acesso a todas as variáveis ​​globais dessa maneira.
Bobort
Não use isso, pois é vulnerável à injeção de script (XSS).
Pcworld 29/08/19
Eles podem validar dados no lado do servidor.
Muhammad Faizan saiu em
4

Eu também tenho lutado com isso. Na superfície, parece que as soluções acima devem funcionar. No entanto, a arquitetura django exige que cada arquivo html tenha suas próprias variáveis ​​renderizadas (ou seja, {{contact}}é renderizado para contact.html, enquanto {{posts}}vai para eg index.htmle assim por diante). Por outro lado, <script>tags aparecem após a na partir do qual e herdar. Isso basicamente significa que qualquer solução, incluindo{%endblock%}base.htmlcontact.htmlindex.html

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

está fadado a falhar, porque a variável e o script não podem coexistir no mesmo arquivo.

A solução simples que acabei encontrando e funcionou para mim foi simplesmente envolver a variável com uma tag com id e depois se referir a ela no arquivo js, ​​da seguinte forma:

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

e depois:

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

e apenas incluem <script src="static/js/somecode.js"></script>em base.htmlcomo de costume. Claro que isso é apenas sobre como obter o conteúdo. Em relação à segurança, basta seguir as outras respostas.

Shoval Sadde
fonte
Você provavelmente deseja usar em .textContentvez de .innerHTML, porque, caso contrário, as entidades que forem codificadas em HTML também farão parte da variável JS. Mas, mesmo assim, pode não ser reproduzido 1: 1 (não tenho certeza).
pcworld 29/08/19
Um esclarecimento . Eu uso uma maneira semelhante para capturar valores de campo em variáveis ​​(usando IDs criados dinamicamente no formulário), e está funcionando (mas apenas para UMA linha de conjunto de formulários ). O que eu não sou capaz de contornar é capturar valores de todas as linhas dos campos do formset , que estão sendo preenchidos manualmente (ou seja, em uma tabela html usando o loop for ). Como você visualiza as variáveis, apenas a última linha do conjunto de formulários é passada para as variáveis ​​e os valores anteriores são substituídos pelos últimos, à medida que o loop for progride pelas linhas do conjunto de formulários. Existe uma maneira de contornar isso?
user12379095
3

Para um objeto JavaScript armazenado em um campo Django como texto, que precisa novamente se tornar um objeto JavaScript inserido dinamicamente no script na página, você precisa usar ambos escapejse JSON.parse():

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

O Django escapejslida com a citação corretamente e JSON.parse()converte a string novamente em um objeto JS.

shacker
fonte
2

Observe que, se você quiser passar uma variável para um script .js externo, precisará preceder sua marca de script por outra marca de script que declare uma variável global.

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

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

data é definido na visualização como de costume no get_context_data

def get_context_data(self, *args, **kwargs):
    context['myVar'] = True
    return context
Daniel Kislyuk
fonte
A parte para declarar uma variável globalmente foi realmente útil.
Rahul
se você renderizar dados em variáveis ​​js a partir de modelos como acima, deverá renderizar na mesma página (torná-los globais) e outro código será separado para o arquivo js. No lado do servidor deve dados válidos porque o usuário pode mudar de navegador.
Muhammad Faizan saiu em
1

Eu uso dessa maneira no Django 2.1 e trabalho para mim e dessa maneira é segura (referência) :

Lado do Django:

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

Lado do HTML:

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

você pode montar o script inteiro em que sua variável de matriz é declarada em uma sequência, da seguinte maneira:

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

que irá gerar uma string da seguinte maneira

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

depois de ter essa sequência, você deve enviá-la para o modelo

views.py

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

no modelo, você o recebe e o interpreta literariamente como código htm, logo antes do código javascript onde você precisa, por exemplo

modelo

{{ prueba|safe  }}

e abaixo desse restante do código, lembre-se de que a variável a ser usada no exemplo é data2

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

Dessa forma, você manterá o tipo de dados, que neste caso é um arranjo

Daniel Muñoz
fonte
Use isso apenas se tiver certeza de que aaaconterá apenas números, caso contrário, XSS (injeção de script) é possível.
PCWorld
0

Há duas coisas que funcionaram para mim dentro do Javascript:

'{{context_variable|escapejs }}'

e outro: no views.py

from json import dumps as jdumps

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

e no html:

{{ message|safe }}
MbeforeL
fonte