Comment définir une valeur d'une variable dans un code de modèle?

216

Dis que j'ai un modèle

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

Lors de son test, il serait utile de définir la valeur de la variable sans toucher au code python qui invoque ce modèle. Je cherche donc quelque chose comme ça

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

Est-ce que quelque chose comme ça existe dans Django?

Alexis
la source

Réponses:

327

Vous pouvez utiliser la withbalise de modèle.

{% with name="World" %}     
<html>
<div>Hello {{name}}!</div>
</html>
{% endwith %}
John
la source
35
mais pouvez-vous changer la valeur de la variable dans le avec?
David 天宇 Wong
2
Il semble que vous ne puissiez pas déclarer un conteneur (j'ai essayé la liste et le tuple) dans une clause with
Vladislav Ivanishin
Si vous devez déclarer une liste, utilisez make_list. docs.djangoproject.com/en/1.9/ref/templates/builtins/#make-list
MrValdez
3
Jinja dit que c'est {% set myvar = value%} pourquoi cela ne fonctionne pas dans django?
holms
3
@holms Parce que Django n'utilise pas Jinja :-) docs.djangoproject.com/en/1.7/topics/templates
elimisteve
50

Créez une balise de modèle:

L'application doit contenir un templatetagsrépertoire, au même niveau que models.py, views.pyetc. Si cela n'existe pas, créez - ne pas oublier le __init__.pyfichier pour vous assurer que le répertoire est traité comme un package Python.

Créez un fichier nommé à l' define_action.pyintérieur du répertoire templatetags avec le code suivant:

from django import template
register = template.Library()

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

Remarque: le serveur de développement ne redémarrera pas automatiquement. Après avoir ajouté le templatetagsmodule, vous devrez redémarrer votre serveur avant de pouvoir utiliser les balises ou les filtres dans les modèles.


Ensuite, dans votre modèle, vous pouvez attribuer des valeurs au contexte comme ceci:

{% 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
la source
2
dans mon cas, après la boucle, cela renvoie l'ancienne valeur :(
holms
7
Dans la dernière version, il semble que vous pouvez utiliser simple_tag au lieu de affectation_tag (et cela a fonctionné pour moi).
Katharine Osborne
Le problème que j'ai rencontré avec cette solution est qu'elle apparaît, que vous ne pouvez pas remplacer les valeurs.
Jakub Jabłoński
si vous souhaitez utiliser cette technique pour définir une liste au lieu d'une simple valeur, vérifiez ceci: stackoverflow.com/a/34407158/2193235
msb
si vous définissez la variable comme un entier et que vous voulez incrémenter (par exemple), vous devez utiliser add: {% define counter|add:1 as counter %}. De même pour les autres opérations.
msb
35

Une autre méthode qui ne nécessite pas que vous mettiez tout dans le bloc "avec" consiste à créer une balise personnalisée qui ajoute une nouvelle variable au contexte. Un péché:

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)

Cela vous permettra d'écrire quelque chose comme ça dans votre modèle:

{% setvar "a string" as new_template_var %}

Notez que la plupart de ces informations ont été prises à partir d'ici

Karim
la source
Que diriez-vous d'affecter des variables à d'autres variables présentes dans le contexte? Et sur une note différente: autoriser les modèles à attribuer arbitrairement des variables de contexte sans vérifier si elles existent déjà peut avoir des implications sur la sécurité. Une approche plus sensée à mon avis serait de vérifier le contexte de la variable avant d'essayer de l'attribuer:
soze
if context.get (self.var_name): lever SuspiciousOperation ("Tentative d'assigner une variable du modèle déjà présent dans le contexte")
soze
27

Il y a des astuces comme celle décrite par John; cependant, le langage de gabarit de Django de par sa conception ne prend pas en charge la définition d'une variable (voir la boîte "Philosophie" dans la documentation de Django pour les modèles ).
Pour cette raison, la méthode recommandée pour modifier une variable consiste à toucher le code Python.

Rob
la source
7
Merci pour le pointeur. Du point de vue d'un concepteur, il est parfois plus facile de définir rapidement une variable pour tester différents états d'une page lors de sa conception. Ne pas suggérer que cette pratique soit utilisée dans un code en cours d'exécution.
Alexis
2
la balise "with" est acceptée dans django1.0. Il semble donc qu'ils modifient enfin leur philosophie :).
Evgeny
2
En fait, la balise "with" est juste pour les alias. Cela peut avoir un impact énorme sur les performances (et sur la lisibilité aussi!), Mais cela ne définit pas vraiment une variable en termes de programmation traditionnelle.
voler
12

La meilleure solution pour cela est d'écrire une coutume assignment_tag. Cette solution est plus propre que l’utilisation d’unwith balise car elle permet une séparation très nette entre la logique et le style.

Commencez par créer un fichier de balises de modèle (par exemple. appname/templatetags/hello_world.py):

from django import template

register = template.Library()

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

Vous pouvez maintenant utiliser la get_addresseebalise de modèle dans vos modèles:

{% load hello_world %}

{% get_addressee as addressee %}

<html>
    <body>
        <h1>hello {{addressee}}</h1>
    </body>
</html>
entité unique
la source
3
Pour les personnes utilisant les nouvelles versions de Django, son appelé simple_tag maintenant! Gagnez du temps pour comprendre pourquoi "s'inscrire .." n'est pas reconnu dans votre code ...
kaya
11

Peut-être que le defaultfiltre de modèle n'était pas une option en 2009 ...

<html>
<div>Hello {{name|default:"World"}}!</div>
</html>
John Mee
la source
Je dois dire que c'est ce que je cherchais! Il peut également être utilisé avec avec : {% with state=form.state.value|default:other_context_variable %}au lieu de, other_context_variablenous pouvons également en utiliser 'string_value'également
Saurav Kumar
Mais il l'imprimera et je dois l'enregistrer pour une utilisation ultérieure
holms
4

Ce n'est pas une bonne idée en général. Faites toute la logique en python et passez les données au modèle pour les afficher. Le modèle doit être aussi simple que possible pour garantir que ceux qui travaillent sur la conception puissent se concentrer sur la conception plutôt que de se soucier de la logique.

Pour donner un exemple, si vous avez besoin d'informations dérivées dans un modèle, il est préférable de les insérer dans une variable dans le code python, puis de les transmettre au modèle.

Sarang
la source
3

Utilisez l' instruction with .

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

Je ne peux pas impliquer le code dans le premier paragraphe de cette réponse . Peut-être que le langage du modèle avait déprécié l'ancien format.

ramwin
la source
2

Dans votre modèle, vous pouvez faire ceci:

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

Dans vos balises de modèle, vous pouvez ajouter une balise comme celle-ci:

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