Gabarit Django comment rechercher une valeur de dictionnaire avec une variable

234
mydict = {"key1":"value1", "key2":"value2"}

La façon régulière à rechercher une valeur dictionnaire dans un modèle Django est {{ mydict.key1 }}, {{ mydict.key2 }}. Et si la clé est une variable de boucle? c'est à dire:

{% for item in list %} # where item has an attribute NAME
  {{ mydict.item.NAME }} # I want to look up mydict[item.NAME]
{% endfor %}

mydict.item.NAMEéchoue. Comment régler ceci?

Stan
la source

Réponses:

362

Écrivez un filtre de modèle personnalisé:

from django.template.defaulttags import register
...
@register.filter
def get_item(dictionary, key):
    return dictionary.get(key)

(J'utilise de .getsorte que si la clé est absente, elle n'en retourne aucune. Si vous le faites, dictionary[key]elle lèvera un KeyErroralors.)

usage:

{{ mydict|get_item:item.NAME }}
culebrón
la source
16
Documentation Django Custom Template Tag , pour ceux qui le trouveront à l'avenir.
Jeff
282
Pourquoi ce n'est pas intégré par défaut? :-(
Berislav Lopac
11
Je pense que @Jeff signifiait la documentation Django Custom Template Filter
Jorge Leitao
5
dans Jinja2 {{mydict [key]}}
Evgeny
9
Le filtre va-t-il dans views.py, quelques filtres supplémentaires.py ou quel fichier?
AlanSE
56

Récupérez à la fois la clé et la valeur du dictionnaire dans la boucle:

{% for key, value in mydict.items %}
    {{ value }}
{% endfor %}

Je trouve cela plus facile à lire et cela évite d'avoir besoin d'un codage spécial. J'ai généralement besoin de la clé et de la valeur à l'intérieur de la boucle.

Paul Whipp
la source
28
Il n'a pas demandé d'énumérer un dict (comme vous le montrez) - il a demandé d'obtenir la valeur du dict en fonction d'une clé variable. Votre proposition ne fournit pas de solution.
staggart
C'est une solution (tout simplement très inefficace) car vous pouvez énumérer les éléments du dict puis les faire correspondre avec la clé de la liste.
DylanYoung
1
Notez que cela ne fonctionne pas si le dictionnaire auquel vous essayez d'accéder contient un autre dictionnaire à l'intérieur.
J0ANMM
Si vos valeurs sont des dict, vous pouvez inclure une autre boucle for pour traiter leurs clés et leurs valeurs, mais il est probable que la complexité vous amène à ce qu'il vaille la peine d'utiliser un filtre personnalisé comme décrit dans la réponse de @ culebron.
Paul Whipp
37

Vous ne pouvez pas par défaut. Le point est le séparateur / déclencheur de la recherche d'attribut / recherche de clé / tranche.

Les points ont une signification particulière dans le rendu des modèles. Un point dans un nom de variable signifie une recherche. Plus précisément, lorsque le système de modèles rencontre un point dans un nom de variable, il essaie les recherches suivantes, dans cet ordre:

  • Recherche de dictionnaire. Exemple: foo ["bar"]
  • Recherche d'attribut. Exemple: foo.bar
  • Recherche d'index de liste. Exemple: foo [bar]

Mais vous pouvez créer un filtre qui vous permet de passer un argument:

https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#writing-custom-template-filters

@register.filter(name='lookup')
def lookup(value, arg):
    return value[arg]

{{ mydict|lookup:item.name }}
Yuji 'Tomita' Tomita
la source
1
return value.get(arg)J'emploierais toujours parce que cela ne lèverait pas une exception KeyError si la clé n'est pas présente.
slajma
3

Pour moi, la création d'un fichier python nommé template_filters.pydans mon application avec le contenu ci-dessous a fait le travail

# coding=utf-8
from django.template.base import Library

register = Library()


@register.filter
def get_item(dictionary, key):
    return dictionary.get(key)

l'usage est comme ce que culebrón a dit:

{{ mydict|get_item:item.NAME }}
AmiNadimi
la source
Pourquoi register = Library()? Qu'est ce que ça fait ?
MD. Khairul Basar
2
Si vous voulez que tous vos modèles connaissent votre nouveau filtre, vous devez l'enregistrer sous django.template.base.Libraryclasse. par register = Library()nous instancions cette classe et utilisons filterl'annotateur de fonction à l'intérieur pour atteindre notre besoin.
AmiNadimi
2

J'avais une situation similaire. Cependant, j'ai utilisé une solution différente.

Dans mon modèle, je crée une propriété qui fait la recherche du dictionnaire. Dans le modèle, j'utilise ensuite la propriété.

Dans mon modèle: -

@property
def state_(self):
    """ Return the text of the state rather than an integer """
    return self.STATE[self.state]

Dans mon modèle: -

The state is: {{ item.state_ }}
sexybear2
la source
1

Comme je ne peux pas commenter, permettez-moi de le faire sous la forme d'une réponse:
pour s'appuyer sur la réponse de culebrón ou sur la réponse de Yuji 'Tomita' Tomita , le dictionnaire passé dans la fonction est sous la forme d'une chaîne, alors utilisez peut-être ast. literal_eval pour convertir d'abord la chaîne en dictionnaire, comme dans cet exemple .

Avec cette modification, le code devrait ressembler à ceci:

@register.filter(name='lookup')
def lookup(value, arg):
    dictionary = ast.literal_eval(value)
    return value.get(arg)

{{ mydict|lookup:item.name }}
BurntIce
la source
0

Environnement: Django 2.2

  1. Exemple de code:


    from django.template.defaulttags import register

    @register.filter(name='lookup')
    def lookup(value, arg):
        return value.get(arg)

J'ai mis ce code dans un fichier nommé template_filters.py dans mon dossier de projet nommé portfoliomgr

  1. Peu importe où vous placez votre code de filtre, assurez-vous que vous avez __init__.py dans ce dossier

  2. Ajoutez ce fichier à la section des bibliothèques dans la section des modèles de votre fichier projectfolder / settings.py. Pour moi, c'est portfoliomgr / settings.py



    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
                'libraries':{
                    'template_filters': 'portfoliomgr.template_filters',
                }
            },
        },
    ]
  1. Dans votre code html chargez la bibliothèque

    
    {% load template_filters %}
Krishna
la source
-2

env: django 2.1.7

vue:

dict_objs[query_obj.id] = {'obj': query_obj, 'tag': str_tag}
return render(request, 'obj.html', {'dict_objs': dict_objs})

modèle:

{% for obj_id,dict_obj in dict_objs.items %}
<td>{{ dict_obj.obj.obj_name }}</td>
<td style="display:none">{{ obj_id }}</td>
<td>{{ forloop.counter }}</td>
<td>{{ dict_obj.obj.update_timestamp|date:"Y-m-d H:i:s"}}</td>
Yi Yang Apollo
la source
1
Le code du modèle {{ dict_obj.obj.obj_name }}est dans ce cas équivalent au code Python dict_obj["obj"]["obj_name"], cependant, la question concerne l'équivalent de dict_obj[obj][obj_name].
Flimm