Comment accéder à un élément de dictionnaire dans un template Django?

181

Je voudrais imprimer le nombre de votes que chaque choix a obtenu. J'ai ce code dans un modèle:

{% for choice in choices %}
    {{choice.choice}} - {{votes[choice.id]}} <br />
{% endfor %}

votesest juste un dictionnaire tandis que choicesest un objet modèle.

Il lève une exception avec ce message:

"Could not parse the remainder"
Mohamed
la source

Réponses:

63

Pour faire écho / prolonger le commentaire de Jeff, je pense que vous devriez viser simplement une propriété de votre classe Choice qui calcule le nombre de votes associés à cet objet:

class Choice(models.Model):
    text = models.CharField(max_length=200)

    def calculateVotes(self):
        return Vote.objects.filter(choice=self).count()

    votes = property(calculateVotes)

Et puis dans votre modèle, vous pouvez faire:

{% for choice in choices %}
    {{choice.choice}} - {{choice.votes}} <br />
{% endfor %}

La balise de modèle, à mon humble avis, est un peu exagérée pour cette solution, mais ce n'est pas non plus une solution terrible. Le but des modèles dans Django est de vous isoler du code dans vos modèles et vice-versa.

J'essaierais la méthode ci-dessus et verrais ce que SQL l'ORM génère car je ne suis pas sûr du haut de ma tête s'il pré-cache les propriétés et crée simplement une sous-sélection pour la propriété ou s'il sera itérativement / sur- demande exécutez la requête pour calculer le nombre de votes. Mais si cela génère des requêtes atroces, vous pouvez toujours remplir la propriété dans votre vue avec les données que vous avez collectées vous-même.

John Ewart
la source
merci @john ewart, votre solution a fonctionné pour moi. Je suis novice en django et python et je ne peux pas comprendre comment obtenir le SQL généré par ORM.
Mohamed
Vous pouvez trouver la réponse à ce problème ici: docs.djangoproject.com/en/dev/faq/models / ... C'est assez simple, en fait et peut être affiché dans votre modèle, ou connecté avec une fonction de journalisation, mais vous devez n'oubliez pas d'activer DEBUG pour que cela fonctionne.
John Ewart
cette solution est parfaite pour un problème que j'ai rencontré avec les modèles django templating + google app engine. J'aimerais pouvoir vous voter deux fois.
Conrad,
5
Bien que cela fonctionne, ce n'est pas très efficace. Il fait des requêtes SQL en boucle (ce que vous devez éviter). Créer votre propre balise pour faire des recherches de dict est facile: @ register.filter def lookup (d, key): if d et isinstance (d, dict): return d.get (key)
dalore
La création d'une classe est bien trop lourde; un dictionnaire mieux structuré, combiné à l' .itemsappel (comme illustré dans l'une des autres réponses) est une solution beaucoup plus simple.
Zags le
285
choices = {'key1':'val1', 'key2':'val2'}

Voici le modèle:

<ul>
{% for key, value in choices.items %} 
  <li>{{key}} - {{value}}</li>
{% endfor %}
</ul>

Fondamentalement, .itemsest un mot-clé Django qui divise un dictionnaire en une liste de (key, value)paires, un peu comme la méthode Python .items(). Cela permet l'itération sur un dictionnaire dans un modèle Django.

russian_spy
la source
@anacarolinats (et autres) assurez-vous simplement de parcourir à la fois la clé, la valeur pour les choix.items. Cela devrait toujours fonctionner.
OldTinfoil
Finalement! Je vous remercie!! : D
djGrill
donc dans le moteur de modèle ne peut pas utiliser (). BTW Merci wotks pour moi.
BlaShadow
6
Belle solution concise à la question. Pour clarifier, itemsest un appel de méthode Python sur le dictionnaire, pas un mot clé Django. Comme le souligne Alex Martelli, c'est fondamentalement le même que iteritems. Comme Wilhelm a répondu, la recherche dans le dictionnaire est la troisième en priorité pour les recherches de points. Si vous avez un élément dans votre dictionnaire nommé 'items', vous récupérerez cette valeur au lieu d'une liste de tuples. Pour tester: ajoutez {'items':'oops'}à votre dictionnaire et vous obtiendrez une liste à puces des lettres du mot 'oops'
cod3monk3y
1
Utilisez collections.OrderedDict pour contrôler l'ordre de l'itération
dnalow
186

vous pouvez utiliser la notation par points:

Les recherches de points peuvent être résumées comme suit: lorsque le système de modèles rencontre un point dans un nom de variable, il essaie les recherches suivantes, dans cet ordre:

  • Recherche dans le dictionnaire (par exemple, foo ["bar"])
  • Recherche d'attribut (par exemple, foo.bar)
  • Appel de méthode (par exemple, foo.bar ())
  • Recherche d'index de liste (par exemple, foo [2])

Le système utilise le premier type de recherche qui fonctionne. C'est une logique de court-circuit.

Wilhelm
la source
44
Dans son cas, le choix est une variable. Faire .choice recherchera la valeur de la clé "choix" plutôt que la valeur du choix de clé.
ibz
+1 pour l'info, même si la question était une sorte de question "devinez ce que je pense". Merci Wilhelm.
eficker
1
Cela fonctionne même avec des dictionnaires imbriqués. Code Python: Code du my_dict[1][2]modèle:my_dict.1.2
djsmith
2
@ JCLeitão Parce que la version correcte est d.key.1- notez la seconde.
Izkata
3
Vérifiez les documents à ce sujet cependant ... à partir de "1.6 docs.djangoproject.com/en/1.6/topics/templates/#variables ": Notez que "bar" dans une expression de modèle comme {{foo.bar}} sera interprété en tant que chaîne littérale et n'utilisant pas la valeur de la variable «bar», s'il en existe une dans le contexte du modèle.
jamesc
25

Vous devez trouver (ou définir) une balise de modèle «get», par exemple ici .

La définition du tag:

@register.filter
def hash(h, key):
    return h[key]

Et il est utilisé comme:

{% for o in objects %}
  <li>{{ dictionary|hash:o.id }}</li>
{% endfor %}
Ned Batchelder
la source
3
considérer à h.get(key,'default_value')cause de KeyError
semiomant
9

Utiliser les éléments du dictionnaire:

{% for key, value in my_dictionay.items %}
  <li>{{ key }} : {{ value }}</li>
{% endfor %}
Priyanshu Chauhan
la source
6

django_template_filter nom du filtre get_value_from_dict

{{ your_dict|get_value_from_dict:your_key }}
Nick Korolkov
la source
6

Similaire à la réponse de @russian_spy:

<ul>
{% for choice in choices.items %} 
  <li>{{choice.0}} - {{choice.1}}</li>
{% endfor %}
</ul>

Cela peut convenir pour décomposer des dictionnaires plus complexes.

Adam Starrh
la source
3

Idéalement, vous créeriez une méthode sur l'objet de choix qui se trouvait dans les votes, ou créeriez une relation entre les modèles. Une balise de modèle qui effectue la recherche dans le dictionnaire fonctionnera également.

Jeff Ober
la source