.. index:: z3c.table, tables Tabellen mit z3c.table ********************** Im Moment ist die Startseite unserer ZContact-Anwendung nicht besonders gut geeignet für die Darstellung einer großen Anzahl von Kontakten. Wenn z. B. 3000 Kontakte vorhanden wären, würde die Startseite sehr lang werden und eine lange Ladezeit haben. Darüberhinaus fehlt eine sortierte Ausgabe nach Vorname oder zuletzt geändert. Die Lösungen der eben beschriebenen Probleme lassen sich mit der Funktionalität von ``z3c.table`` realisieren, welches die Sortierung von Spalten, eine einegebaute Stapel-Verarbeitung (batching) durch Erweiterung und Anpassung erlaubt. Lassen Sie uns beginnen. Wie üblich, ist der erste Schritt, die Abhängingkeit von ``z3c.table`` in der Datei ``setup.py`` einzutragen. Danach muß der buildout-Prozess neu durchlaufen werden, der das neue Paket installiert:: $ ./bin/buildout -N Nicht vergessen die zcml-Konfiguration in ``zcontact/configure.zcml`` einzutragen. .. code-block:: xml Eine Tablle erzeugen -------------------- Jetzt können Sie ``zcontact/browser/contact.py`` editieren um eine Tabelle zu erzeugen. Zunächst importieren Sie ``z3c.table`` und hier sind wir an den Modulen ``table`` and ``column`` interessiert. .. code-block:: python from z3c.table import table, column Wenn die Klasse ``FrontPage`` von der Klasse ``Table`` erbt, sollten alle Tabellenfunktionalitäten in unseren Ansichten verfügbar sein. Uns so sollte es aussehen: .. code-block:: python class FrontPage(BrowserPagelet, table.Table): """Pagelet for the front page.""" update = table.Table.update Beachten Sie die Reihenfolge der Vererbung, die von Bedeutung ist! Beide Klassen ``BrowserPagelet`` und ``Table`` haben die Methoden ``update`` und ``render``. Die ``render`` Methode von ``BrowserPagelet`` soll weiter genutzt werden, weil sie automatisch das ``frontpage.pt``-Template findet, das wir mit zcml registriert haben. Von ``Table`` wollen wir die update-Methode nutzen, weil sie die Tabellen-Zellen mit Daten füllt. Es ist zu betonen, daß beide Klassen eine ``__init__``-Methode besitzen und damit den Context und den Request als Parameter erhalten. Die Klasse ``Table`` nutzt den Context als ein Container-Objekt deren Werte zur Erzeugung der Zeilen einer Tabelle genutzt werden. Jetzt muß ``zcontact/browser/frontpage.pt`` geändert werden, damit die Tabelle dort eingefügt wird, wo bisher unsere Liste erschien. Das Template sollte etwa so aussehen: .. code-block:: python

Welcome to ZContact

Please tell me what you would like to do:

Die Klasse ``Table`` verwendet die Methode ``renderTable`` um den html-Code einer Tabelle zu erzeugen. Diese Methode wird mit dem Schlüsselwort ``structure`` aufgerufen, um die Struktur der html-Ausgabe zu erhalten. Wenn Sie jetzt den Server neu starten und die Startseite betrachten, sollten Sie sehen, daß etwas fehlt! Wir haben noch keine Spalten definiert. Spalten erzeugen ---------------- Beginnen Sie mit der Erzeugung von zwei Spalten für die Kontakte, mit Nachname und Vorname. Tabellenspalten werden als eigenständige Objekte unter Verwendung des Interfaces ``z3c.table.interfaces.IColumn`` implentiert. Wir erreichen das, durch die Verwendung von Basis-Klassen die wir erweitern. Eine solche Basis-Klasse ist ``GetAttrColumn``. Diese füllt Zellen mit Werten eines bestimmten Attributes. Damit lassen sich sehr schnell die zwei Spalten definieren: .. code-block:: python class FirstNameColumn(column.GetAttrColumn): header = u'First Name' attrName = 'firstName' weight = 1 class LastNameColumn(column.GetAttrColumn): header = u'Last Name' attrName = 'lastName' weight = 2 Die Klasse ``GetAttrColumn`` verwendet den Wert von ``attrName`` um die Eigenschaft zu finden. Der ``header`` wird verwendet um eine Spaltenüberschrift zu erzeugen. Die Eigenschaft ``weight`` wird verwendet, um die Reihenfolge der Spalten festzulegen, in unserem Fall in der Reihenfolge: Vorname, Nachname. Um diese Spalten zur Tabelle hinzufügen zu können, müssen Sie als benannte Multiadapter registriert werden. Wenn die Tabelle gerendert wird, werden die Spalten mit Unterstützung von Context, Request und Tabelle nachgeschlagen. Das macht die Tabelle modular (plugable). Fügen Sie die folgenden Elemente in die Datei ``zcontact/browser/configure.zcml`` ein. .. code-block:: xml Wir definieren hier, das die Spalten registriert werden, wenn sie für einen Context der ``IRootFolder`` und Requests die ``IZContactBrowserLayer`` und Tabellen die ``ITable`` unterstützen. Das bedeutet, wir könnten Tabellen mit unterschiedlichen Spalten für unterschiedliche Skins erzeugen. Ein Administrations-Skin könnte mehr Informationen zeigen, es gibt unendlich viele Möglichkeiten. Wir benutzen hier das erstemal den ``zope`` Namensraum, in der Datei ``zcontact/browser/configure.zcml``, weshalb er in configure-Element deklariert werden muß. Es sollte dann so aussehen: .. code-block:: xml Jetzt kann der Server gestartet werden und folgendes ist zu sehen (Es sieht vielleicht etwas anders aus, weil ich ein wenig css verwendet habe). .. image:: images/TableContents1.png Zellinhalte anpassen -------------------- Vor der Verwendung einer Tabelle, waren unsere Kontakte Verweise auf das Kontakt-Anzeige-Formular. Wir wollen diese Verweise wieder aktivieren, indem die Erzeugung der Zellinhalt angepasst wird. Wir könnten das unter Verwendung der Klasse ``z3c.table.column.LinkColumn`` tun, wollen aber zur Demonstartion eine eigene Lösung verwenden. Alles was getan werden muß ist die Methode ``renderCell`` zu überschreiben. Das ist unglaublich einfach, wenn Sie den Code wie gezeigt in ``zcontact/browser/contact.py`` anpassen: .. code-block:: python class FirstNameColumn(column.Column): header = u'First Name' weight = 1 def renderCell(self, item): return '%s' % (absoluteURL(item, self.request), item.firstName) class LastNameColumn(column.Column): header = u'Last Name' weight = 2 def renderCell(self, item): return '%s' % (absoluteURL(item, self.request), item.lastName) Beachten Sie, daß ich nicht mehr von ``column.GetAttrColumn`` erbe, weil wir diese Funktionalitäten nicht benötigen. Modulare Tabellen ----------------- Nun wollen wir einen Schritt weiter gehen und weitere Daten in der Kontakt-Tabelle zeigen, wie das Datum der Neuanlage eines Kontaktes oder das Datum der letzten Änderung. Wir haben Glück, denn es hat schon jemand eine generische Klasse geschrieben, die diesen Datentyp in einer Spalte darstellt. Da die Tabellen-Maschinerie die Zope-Komponenten-Archichitektur nutzt, brauchen wir nur die zusätzlichen Spalten in zcml zu registrieren und wir sind fertig. Ich habe die folgenden zwei zcml-Deklarationen zu ``zcontact/browser/configure.zcml`` hinzugefügt: .. code-block:: xml Diese Spalten nutzen das Dublin-Core-Metadaten-System von Zope um das Erstellungs- oder Änderungsdatum zu erhalten. Dublin-Core funktioniert nur für Daten die annotiert werden können, d.h.diese Objekte können über IAnnotions adaptiert werden. Damit die Kontakt-Objekte automatisch diese Metadaten unterstützen, muß die Kontakt-Klasse ``IAttributeAnnotatable`` implementieren. Das kann mit der zcml-Direktive ``implements`` erreicht werden, die dann wie folgt aussieht: .. code-block:: xml Starten Sie nun den Server und sie werden etwa das folgende sehen: .. image:: images/TableContents2.png Spaltenweise Sortierung ----------------------- Was ist eine Tabelle ohne Spaltensortierung? Alles was wir für die Sortierung brauchen ist die Methode ``getSorkKey`` in der Spalten-Klasse. Eine solche Methode sieht wie folgt aus: class FirstNameColumn(column.Column): header = u'First Name' weight = 1 def getSortKey(self, item): return item.firstName def renderCell(self, item): return '%s' % (absoluteURL(item, self.request), item.firstName) class LastNameColumn(column.Column): header = u'Last Name' weight = 2 def getSortKey(self, item): return item.lastName def renderCell(self, item): return '%s' % (absoluteURL(item, self.request), item.lastName) Nach dem Serverneustart können auf der Startseite die Spalten über die Variablen des Request sortiert werden. Der Request, verwendet zwei Variablen, die vom Namen der Tabelle abhängen. Versuchen Sie z. B. die Tabelle nach dem Nachnamen zu sortieren, verwende Sie dazu die folgende URL: http://localhost:8080/++skin++ZContact/@@index.html?table-sortOrder=ascending&table-sortOn=table-lastName-1 Nachdem die Sortierung aktiviert ist, müssen die Sortieroptionen in die Benutzerführung integriert werden. Eine Möglichkeit ist es, die Überschrift einer jeden Spalte mit einer korrekten URL zu versehen. Dafür gibt es eine Klasse ``SortingColumnHeader`` die genau das macht. Alles was zu tun ist, ist eine Adapter zu registrieren. Er nimmt Context, Request, Tabelle und Spalte als Argumente und unterstützt die ``IColumnHeader``-Klasse. Die Registrierung in ``zcontact/browser/configure.zcml`` sieht wie folgt aus: .. code-block:: xml Nun, kann nach dem Serverneustart durch klicken, die magische Sortierung der Spalten beobachtet werden.