Como definir um valor de uma variável dentro de um código de modelo?

216

Digamos que eu tenho um modelo

<html>
<div>Hello {{name}}!</div>
</html>

Durante o teste, seria útil definir o valor da variável sem tocar no código python que chama esse modelo. Então, eu estou procurando algo como isto

{% set name="World" %}     
<html>
<div>Hello {{name}}!</div>
</html>

Existe algo assim no Django?

Alexis
fonte

Respostas:

327

Você pode usar a withtag template.

{% with name="World" %}     
<html>
<div>Hello {{name}}!</div>
</html>
{% endwith %}
John
fonte
35
mas você pode alterar o valor da variável no with?
David天宇Wong
2
Parece que você não pode declarar um recipiente (que eu lista e tupla tentou) em um com a cláusula
Vladislav Ivanishin
Se você precisar declarar uma lista, use make_list. docs.djangoproject.com/en/1.9/ref/templates/builtins/#make-list
MrValdez
3
Jinja diz que é {% set myvar = value%} por que não funciona no django?
holms
3
@holms Porque Django não usa Jinja :-) docs.djangoproject.com/en/1.7/topics/templates
elimisteve
50

Crie uma tag de modelo:

O aplicativo deve conter um templatetagsdiretório, no mesmo nível que models.py, views.pyetc. Se isso ainda não existir, crie-o - não esqueça o __init__.pyarquivo para garantir que o diretório seja tratado como um pacote Python.

Crie um arquivo nomeado define_action.pydentro do diretório templatetags com o seguinte código:

from django import template
register = template.Library()

@register.simple_tag
def define(val=None):
  return val

Nota: O servidor de desenvolvimento não será reiniciado automaticamente. Depois de adicionar o templatetagsmódulo, você precisará reiniciar o servidor antes de poder usar as tags ou filtros nos modelos.


Em seu modelo, você pode atribuir valores ao contexto da seguinte maneira:

{% load define_action %}
{% if item %}

   {% define "Edit" as action %}

{% else %}

   {% define "Create" as action %}

{% endif %}


Would you like to {{action}} this item?
Mods Vs Rockers
fonte
2
no meu caso, após o loop, isso retorna valor antigo :(
holms
7
Na versão mais recente, parece que você pode usar simple_tag em vez de assignment_tag (e funcionou para mim).
Katharine Osborne
O problema que obtive com esta solução é que parece que você não pode substituir valores.
Jakub Jabłoński 13/03
se você quiser usar esta técnica para definir uma lista em vez de apenas um valor, verifique o seguinte: stackoverflow.com/a/34407158/2193235
msb
se você estiver configurando a variável como um inteiro e você quiser incrementá-lo (por exemplo), você precisa usar add: {% define counter|add:1 as counter %}. Da mesma forma para outras operações.
msb 13/06
35

Uma maneira alternativa que não exige que você coloque tudo no bloco "com" é criar uma tag personalizada que adicione uma nova variável ao contexto. Como em:

class SetVarNode(template.Node):
    def __init__(self, new_val, var_name):
        self.new_val = new_val
        self.var_name = var_name
    def render(self, context):
        context[self.var_name] = self.new_val
        return ''

import re
@register.tag
def setvar(parser,token):
    # This version uses a regular expression to parse tag contents.
    try:
        # Splitting by None == splitting by spaces.
        tag_name, arg = token.contents.split(None, 1)
    except ValueError:
        raise template.TemplateSyntaxError, "%r tag requires arguments" % token.contents.split()[0]
    m = re.search(r'(.*?) as (\w+)', arg)
    if not m:
        raise template.TemplateSyntaxError, "%r tag had invalid arguments" % tag_name
    new_val, var_name = m.groups()
    if not (new_val[0] == new_val[-1] and new_val[0] in ('"', "'")):
        raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name
    return SetVarNode(new_val[1:-1], var_name)

Isso permitirá que você escreva algo assim no seu modelo:

{% setvar "a string" as new_template_var %}

Observe que a maior parte disso foi tirada daqui

Karim
fonte
Que tal atribuir variáveis ​​a outras variáveis ​​presentes no contexto? E em outra nota: permitir que os modelos atribuam arbitrariamente variáveis ​​de contexto sem verificar se elas já existem pode ter implicações de segurança. Uma abordagem mais sensata na minha opinião seria a de verificar o contexto para a variável antes de tentar atribuí-lo:
Soze
if context.get (self.var_name): raise SuspiciousOperation ("Tentativa de atribuir variável do modelo já presente no contexto")
soze
27

Existem truques como o descrito por John; no entanto, a linguagem de modelo do Django por design não suporta a configuração de uma variável (consulte a caixa "Filosofia" na documentação do Django para obter modelos ).
Por esse motivo, a maneira recomendada de alterar qualquer variável é através do toque no código Python.

roubar
fonte
7
Obrigado pelo ponteiro. Da perspectiva de um designer, às vezes é mais fácil definir rapidamente uma variável para testar vários estados de uma página ao projetá-la. Não sugerindo que esta prática seja usada em um código em execução.
Alexis
2
a tag "with" é aceita no django1.0. Então parece que eles estão finalmente alterando sua filosofia :).
Evgeny
2
Por uma questão de fatos, a tag "with" é apenas para aliases. Isso pode ter um enorme impacto no desempenho (e também na legibilidade!), Mas na verdade não está definindo uma variável nos termos de programação tradicionais.
Rob
12

A melhor solução para isso é escrever um costume assignment_tag. Essa solução é mais limpa do que usar uma withtag, pois consegue uma separação muito clara entre lógica e estilo.

Comece criando um arquivo de tag de modelo (por exemplo appname/templatetags/hello_world.py):

from django import template

register = template.Library()

@register.assignment_tag
def get_addressee():
    return "World"

Agora você pode usar a get_addresseetag template nos seus modelos:

{% load hello_world %}

{% get_addressee as addressee %}

<html>
    <body>
        <h1>hello {{addressee}}</h1>
    </body>
</html>
entidade única
fonte
3
Para quem usa as versões mais recentes do Django, chama-se simple_tag now! Salvar o tempo para descobrir por que o "registo .." não é reconhecido em seu código ...
kaya
11

Talvez o defaultfiltro de modelo não fosse uma opção em 2009 ...

<html>
<div>Hello {{name|default:"World"}}!</div>
</html>
John Mee
fonte
Devo dizer que era isso que eu estava procurando! Ele pode também ser usado com com : {% with state=form.state.value|default:other_context_variable %}em vez de other_context_variablenós também pode usar qualquer 'string_value'bem
Saurav Kumar
Mas vai imprimi-lo, e eu preciso salvá-lo para uso posterior
holms
4

Esta não é uma boa ideia em geral. Faça toda a lógica em python e passe os dados para o modelo para exibição. O modelo deve ser o mais simples possível para garantir que aqueles que trabalham no design possam se concentrar no design, em vez de se preocupar com a lógica.

Para dar um exemplo, se você precisar de algumas informações derivadas em um modelo, é melhor inseri-las em uma variável no código python e depois passá-las para o modelo.

Sarang
fonte
3

Use a instrução with .

{% with total=business.employees.count %}
    {{ total }} employee{{ total|pluralize }}
{% endwith %}

Não posso sugerir o código no primeiro parágrafo desta resposta . Talvez a linguagem do modelo tenha descontinuado o formato antigo.

Ramwin
fonte
2

No seu modelo, você pode fazer assim:

{% jump_link as name %}
{% for obj in name %}
    <div>{{obj.helo}} - {{obj.how}}</div>
{% endfor %}

Nas suas tags de modelo, você pode adicionar uma tag como esta:

@register.assignment_tag
def jump_link():
    listArr = []
    for i in range(5):
        listArr.append({"helo" : i,"how" : i})
    return listArr
Ashish Gupta
fonte