Die Django-Vorlage kann das Standarddict nicht wiederholen


70
import collections

data = [
  {'firstname': 'John', 'lastname': 'Smith'}, 
  {'firstname': 'Samantha', 'lastname': 'Smith'}, 
  {'firstname': 'shawn', 'lastname': 'Spencer'}, 
]

new_data = collections.defaultdict(list)

for d in data:
    new_data[d['lastname']].append(d['firstname'])

print new_data

Hier ist die Ausgabe:

defaultdict(<type 'list'>, {'Smith': ['John', 'Samantha'], 'Spencer': ['shawn']})

und hier ist die Vorlage:

{% for lastname, firstname in data.items %}
  <h1> {{ lastname }} </h1>
  <p> {{ firstname|join:", " }} </p>
{% endfor %}

Aber die Schleife in meiner Vorlage funktioniert nicht. Es zeigt sich nichts. Es gibt mir nicht einmal einen Fehler. Wie kann ich das beheben? Es soll den Nachnamen zusammen mit dem Vornamen anzeigen, ungefähr so:

<h1> Smith </h1>
<p> John, Samantha </p>

<h1> Spencer </h1>
<p> shawn </p>

Sie haben den Code nicht angezeigt, mit dem das Wörterbuch in den Kontext für die Vorlage eingefügt wird. Bist du sicher, dass das richtig passiert?
Ned Batchelder

Ja, alles andere wird außerhalb der Schleife korrekt gerendert.
user216171

Antworten:



91

Sie können das Kopieren in ein neues Diktat vermeiden, indem Sie die Standardfunktion von defaultdict deaktivieren, sobald Sie neue Werte eingefügt haben:

new_data.default_factory = None

Erläuterung

Das Algorithmus zur variablen Auflösung von Vorlagen in Django versucht new_data.itemsals new_data['items']erstes aufzulösen , der bei Verwendung von defaultdict (Liste) in eine leere Liste aufgelöst wird .

Um die Standardeinstellung für eine leere Liste zu deaktivieren und Django auszuschalten und new_data['items']die Auflösungsversuche bis zum Aufruf fortzusetzen, kann new_data.items()das Attribut default_factory von defaultdict auf None festgelegt werden .


6
+1 - Dies ist viel effizienter als die ausgewählte Antwort, insbesondere bei einem großen Wörterbuchsatz.
Keithhackbarth

Tolle Antwort, Erklärung hat viel zum Verständnis des zugrunde liegenden Problems beigetragen!
Blackeagle52

Ich könnte hinzufügen, dass ein {% für k, v in detauldictvar%} es Ihnen ermöglicht, das Element des defaultdict zu iterieren, auch ohne die default_factory auf None zu setzen. Mit der Erklärung von Sebastien kann ich jetzt verstehen, warum :-)
RobM

1

Da das "Problem" noch Jahre später besteht und der Funktionsweise von Django-Vorlagen eigen ist, schreibe ich lieber eine neue Antwort mit den vollständigen Details, warum dieses Verhalten unverändert bleibt.

So beheben Sie den Fehler

Zunächst besteht die Lösung darin, das defaultdictin a dictumzuwandeln, bevor es an den Vorlagenkontext übergeben wird:

context = {
    'data': dict(new_data)
}

Sie sollten nicht verwenden defaultdict in Django Objekte im Vorlagenkontext verwenden.

Aber wieso?

Der Grund für diesen "Fehler" wird in der folgenden Django-Ausgabe Nr. 16335 beschrieben :

In der Tat läuft es darauf hinaus, dass die Vorlagensprache dieselbe Syntax für die Suche nach Wörterbüchern und Attributen verwendet.

... und aus den Dokumenten :

Wörterbuchsuche, Attributsuche und Listenindexsuche werden mit einer Punktnotation implementiert. [...] Wenn eine Variable in eine aufrufbare Variable aufgelöst wird, ruft das Vorlagensystem sie ohne Argumente auf und verwendet ihr Ergebnis anstelle der aufrufbaren.

Wenn Django Ihren Vorlagenausdruck auflöst , wird es zuerst versuchtdata['items'] . ABER dies ist ein gültiger Ausdruck, der automatisch einen neuen Eintrag itemsin Ihrem Standarddikt erstellt data, mit einer leeren Liste initialisiert (im Fall des ursprünglichen Autors) und die erstellte Liste zurückgibt (leer).

Die beabsichtigte Aktion wäre, die Methode itemsohne Argumente der Instanz aufzurufen data(kurz :) data.items(), aber da dies data['items']ein gültiger Ausdruck war, stoppt Django dort und erhält die gerade erstellte leere Liste.

Wenn Sie den gleichen Code versuchen, aber mit data = defaultdict(int), erhalten Sie einen TypeError: 'int' object is not iterable, da Django nicht in der Lage ist, über den Wert "0" zu iterieren, der durch die Erstellung des neuen Eintrags von zurückgegeben wird defaultdict.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.