.. 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.