.. index:: z3c.form, z3c, formui, form, formui
Formulare mit z3c.form
**********************
Der nächste logische Schritt ist das Hinzufügen von neuen Kontakten mit
einem Formular für Vorname und Nachname.
Dieses Formular kann mit dem Schema (``IContact``) aus ``interfaces.py``
generiert werden, wenn Sie die ``z3c.form``-Komponente verwenden.
.. index:: buildout
Verfügbar machen von z3c.form and z3c.formui
============================================
Vor der Verwendung von z3c.form muss die Komponente in die
Anwendung integriert werden.
Dies erreichen Sie, indem Sie die Datei ``setup.py`` öffnen und
``'z3c.form' zum ``install_requires``-Parameter (etwa bei Zeile 25)
hinzufügen. Für die Darstellung des Layouts sollten auch noch die
Komponenten ``z3c.formui`` und ``z3c.layer`` in das System integriert
werden.
Danach muss noch die Konfiguration für ``z3c.form`` und ``z3c.formui``
in der Datei ``zcontact/src/zcontact/configure.zcml`` erfolgen.
``z3c.form`` nutzt zahlreiche andere z3c-Komponenten, welche wiederum
neue zcml-Direktiven zur Verfügung stellen. Um diese Direktiven später nutzen zu
können, sind die Meta-Dateien am Anfang von ``configure.zcml``, *noch vor
den anderen include-Anweisungen* einzufügen:
.. sourcecode:: xml
Danach sollten dann am Ende der Datei ``configure.zcml`` die Komponenten
``z3c.form`` and ``z3c.formui`` eingefügt werden:
.. sourcecode:: xml
Sie müssen jetzt den Buildout-Prozess erneut starten,
damit die neuen egg-Pakete für ``z3c.form`` und ``z3c.formui``
heruntergeladen und verfügbar gemacht werden. Starten Sie den Vorgang
einfach mit::
$ ./bin/buildout -Nv
Mit der Option ``-N`` verhindern Sie das nochmalige Herunterladen von
Paketen, die Sie bereits haben (und das ist nur zu empfehlen).
Konfiguration der Anwendung für die Benutzung von z3c.form
===========================================================
Leider ist die Nutzung von z3c.form noch nicht möglich.
Weil die z3c-Komponenten ein etwas anderes Muster für die
Anwendungsentwicklung verwenden, sind einige zusätzliche
Arbeitsschritte notwendig, um eigene Anwendungen zum Laufen zu bringen.
Einer dieser Schritte ist die Erstellung eines eigenen Layer und Skin.
Leider zwingen Sie die meisten zcml-Direktiven nicht zur direkten
Angabe des zu verwendenden Layer, wenn eine Seite oder Sicht (View)
registriert wird. Wenn Sie keinen Layer angeben, wird die Seite für
den Standard-Layer erzeugt. Es bleibt festzuhalten, dass viele
Komponenten (inclusive Rotterdam) ihre Sichten für den Standard-Layer
registrieren, was diesen Layer mit unötigen Dingen überfrachtet, die
nicht gebraucht werden. Um diese Überfrachtung zu vermeiden
registrieren alle z3c-Komponenten ihre Sichten an speziellen
komponenten-spezifischen Layern. Die mit den z3c.*-Komponenten
gelieferten Layer müssen für die eigenen Komponenten erweitert werden.
Deshalb erstellen Sie einen eigenen Layer, der es erlaubt, die
Sichten der z3c-Komponente zu verwenden.
.. index:: z3c.layer
Definition eines Layer
-----------------------
Erstellen Sie eine neue Datei , ``src/zcontact/layer.py`` und fügen Sie
den folgenden Code hinzu:
.. sourcecode:: python
from z3c.form.interfaces import IFormLayer
from z3c.layer.pagelet import IPageletBrowserLayer
class IZContactBrowserLayer(IFormLayer, IPageletBrowserLayer):
"""ZContact browser layer with form support."""
``IFormLayer`` hat Sichten für alle Widgets die in den generierten Formularen
verwendet werden, und ``IPageletBrowserLayer`` liefert einige Fehler-Seiten und
andere nützliche Ausgaben.
.. index:: skin
Einen Skin erstellen
--------------------
Für den Zugriff auf den Layer über den Browser wird ein Skin verwendet.
Erstellen Sie dazu eine weitere Datei, ``src/zcontact/skin.py`` und fügen
Sie den folgenden Code ein:
.. sourcecode:: python
import z3c.formui.interfaces
from zcontact import layer
class IZContactBrowserSkin(z3c.formui.interfaces.IDivFormLayer,
layer.IZContactBrowserLayer):
"""The ZContact browser skin using the div-based layout."""
Beachten Sie, dass unser Skin vom ``IDivFormLayer`` erbt, der in der
``z3c.formui`` Komponente definiert ist.
Wenn die Formulare erzeugt werden, sind die Felder in ``
`` Tags
und nicht in einer Tabelle eingebettet.
Es gibt einen weiteren Layer, für ein tabellenbasiertes Layout.
Dank der Komponenten-Architektur wäre es auch möglich einen eigenen
Layout-Layer zu schreiben, aber das soll jetzt gezeigt werden.
Nun müssen Sie den Skin mit einer neuen Datei ``src/zcontact/skin.zcml``
registrieren. Der Skin kann dann wie folgt verwendet werden:
http://localhost:8080/++skin++ZContact/
und der Inhalt der Datei sieht wie folgt aus:
.. sourcecode:: xml
Bitte nicht vergessen in die neue zcml-Datei
``zcontact/configure.zcml`` die folgende Zeile einzufügen:
``
``.
.. index:: add form
Ein Formular für die Neuanlage
==============================
Sie beginnen mit der Erstellung eines neuen Modules ``zcontact.browser``,
durch Anlage eines Ordners unterhalb von ``src/zcontact/``. Der Ordner
``browser`` erhält eine leere Datei ``__init__.py`` . Danach erstellen Sie
die Datei ``zcontact/browser/contact.py`` in der alle Formulare definiert
werden.
Beginnen Sie mit dem Einfügen des folgenden Codes in die Datei
``browser/contact.py``:
.. sourcecode:: python
from z3c.form import form, field
from zcontact import interfaces
class ContactAddForm(form.AddForm):
"""A simple add form for contacts."""
fields = field.Fields(interfaces.IContact)
Die Klasse ``form.AddForm`` von der geerbt wird, definiert einige Methoden,
für das Hinzufügen und Anlegen unserer neuen Objekte, die Sie später
überschreiben werden und einige typische Schalter für ein solches Formular.
Als nächstes wird eine Seite erstellt, die unsere Klasse verwendet und das
Formular zur Anzeige bringt.
Öffnen Sie die Datei ``zcontact/browser/configure.zcml`` und fügen Sie
folgendes hinzu:
.. sourcecode:: xml
Das Browser-Modul muß nun am Ende der Datei
``zcontact/configure.zcml`` durch Hinzufügen der folgenden Zeile
``
`` bekannt gemacht werden.
Für den Test wird jetzt der Server mit ``./bin/paster serve deploy.ini``
(oder ``debug.ini`` wenn Sie möchten) gestartet und im Browser die
folgende Seite aufgerufen:
http://localhost:8080/++skin++ZContact/@@addContact.html.
Hier wird eine Seite mit dem Namen addContact.html für das
IFolder-Interface registriert. Weil jeder Root-Ordner das Interface IFolder
implementiert, sollte das folgende Formular angezeigt werden:
.. image:: images/addFormSimple.png
Das Formular vollenden
======================
Wenn Sie gerade versucht haben das Formular abzuschicken, indem Sie
auf den add-Schalter geklickt haben, sollten Sie eine Fehlermeldung
``NotImplemented`` erhalten haben. Wenn Sie paster mit debug.ini statt
deploy.ini starten, erhalten Sie vielleicht eine etwas schönere Ausgabe
wie hier gezeigt:
.. image:: images/serverError.png
Mit dieser Ausgabe können Sie jede Zeile im traceback erweitern und
Python-Code eingeben um das Probem zu untersuchen. Ich war sehr
beeindruckt, als ich diese Möglichkeit das erste mal sah.
Um das Problem beheben zu können, müssen drei Methoden für die Klasse
``ContactAddForm`` implementiert werden: ``create``, ``add`` und ``nextURL``.
Um es kurz zu halten, kann es wie folgt implementiert werden, Sie können
aber auch einen anderen Weg wählen.
.. sourcecode:: python
from z3c.form import form, field
from zcontact import interfaces
from zcontact.contact import Contact
class ContactAddForm(form.AddForm):
"""A simple add form for contacts."""
fields = field.Fields(interfaces.IContact)
def create(self, data):
contact = Contact()
form.applyChanges(self, contact, data)
return contact
def add(self, contact):
self._name = "%s-%s" % (contact.lastName.lower(), contact.firstName.lower())
self.context[self._name] = contact
def nextURL(self):
return '/'
In der create-Methode, beutzen Sie ``form.applyChanges`` zum Speichern
der Attribute für ``firstName`` und ``lastName`` eines neuen Kontakt-Objektes.
Die Daten, die an die create-Methode übergeben werden, sind eine Zuordnung von
Feldnahmen zu den eingegebenen Werten (Mapping) und bereits in die richtigen
Python-Datentypen konvertiert. Es gibt auch folgende Möglichkeit:
``contact.firstName = data['firstName']``.
In der ``nextURL``-Methode ist ein hart-codierter Pfad anzugeben, der die Anwendung
zum Rotterdam-Standard-Skin umschaltet und dort sollten Sie in der Inhalts-Ansicht
den neu erstellten Kontakt sehen können. Dies ist erforderlich, weil wir noch keine
eigene Inhalts-Ansicht für den eigenen Layer/Skin geschrieben haben.
.. index:: display form, edit form
Formulare für die Anzeige und Bearbeitung
=========================================
Anzeige- und Bearbeitungs-Formulare sind einfacher als Formulare für die Neuanlage,
weil keine Methoden implementiert werden müssen. Beginnen Sie mit einem
Anzeige-Formular.
Erstellung eines Anzeigeformulars
---------------------------------
Für das Anzeige-Formular wird eine neue Klasse benötigt, die von ``form.Form`` erbt.
Um die Formular-Bestandteile (Widgets) als reinen Text und nicht als Eingabefelder
darstellen zu können, muß der Modus für das Formular auf ``DISPLAY_MODE`` gesetz werden.
Es ist eine Konstante, die von ``z3c.form.interfaces`` importiert werden kann.
Öffnen Sie ``zcontact/browser/contact.py`` und fügen Sie den folgenden Code ein:
.. sourcecode:: python
class ContactDisplayForm(form.Form):
"""A simple display form for contacts."""
fields = field.Fields(interfaces.IContact)
mode = DISPLAY_MODE
Vergessen Sie nicht, das neue Formular in ``configure.zcml`` wie folgt zu registrieren:
.. sourcecode:: xml
Nun, können Sie die ``nextURL``-Methode von ``ContactAddForm``
auf das neu erstellte Kontakt-Objekt zeigen lassen. Der Code
sollte wie folgt aussehen:
.. sourcecode:: python
def nextURL(self):
return absoluteURL(self.context[self._name], self.request)
Vergessen Sie bitte nicht die folgende Zeile am Anfang der Datei einzufügen:
``from zope.traversing.browser.absoluteurl import absoluteURL``
Wenn soweit alles geklappt hat, sollte man nach einem Server-Neustart,
mit der folgenden URL einen neuen Kontakt angelegt könenen, der dann
zum Anzeige-Forumlar weitergeleitet wird:
http://localhost:8080/++skin++ZContact/@@addContact.html
Es sollte dann wie hier gezeigt aussehen:
.. image:: images/DisplayFormScreenShot.png
.. index:: buttons
Schalter zum Formular hinzufügen
--------------------------------
Nun wird das Formular um zwei Schalter ergänzt, einen
zum Bearbeiten und den anderen zum Löschen eines Kontakt-Objektes.
Beginnen Sie mit dem Hinzufügen der folgenden import-Anweisung am
Anfang der Datei ``contact.py``.
Wenn der Anwender auf einen der Schalter klickt, schickt er die
Formulardaten zu einem definierten Attribut des Formulars.
Standardmäßig wird die URL des Formulars selbst verwendet,
wenn ein Schalter gedrückt wird und das Formular wird neu geladen.
Wenn das Formular verarbeitet wird, prüft es, welcher Schalter
gedrückt wurde und ruft den passenden "Akteur" auf, der als
Methode der ``ContactDisplayForm``-Klasse definiert wird.
Mit z3c.form definieren wir einen Schalter und den dazugehörigen
Akteur durch Verwendung eines Dekorators.
Der Löschen-Schalter soll den Kontakt entfernen und zum
Hinzufügen-Formular zurückkehren. Deshalb fügen Sie bitte den folgenden
Code zur Klasse ``ContactDisplayForm`` hinzu:
.. sourcecode:: python
@button.buttonAndHandler(u'Delete', name='delete')
def handleDelete(self, action):
name = getName(self.context)
parent = getParent(self.context)
del parent[name]
nextURL = absoluteURL(parent, self.request)+'/@@addContact.html'
self.request.response.redirect(nextURL)
Am Anfang der Datei werden noch die folgenden Import-Anweisungen benötigt:
.. sourcecode:: python
from z3c.form import form, field, button
from z3c.form.interfaces import DISPLAY_MODE
from zope.traversing.browser.absoluteurl import absoluteURL
from zope.traversing.api import getParent, getName
Jetzt fügen Sie noch einen Bearbeiten-Button ('Edit') hinzu.
Dafür gibt es noch kein Bearbeiten-Formular, dieser Schritt wir hier aber
schon vorbereitet. Fügen Sie deshalb folgendes zur
``ContactDisplayForm``-Klasse hinzu:
.. sourcecode:: python
@button.buttonAndHandler(u'Edit', name="edit")
def handleEdit(self, action):
nextURL = absoluteURL(self.context, self.request) + '/@@editContact.html'
self.request.response.redirect(nextURL)
Starten Sie jetzt den Server neu und testen Sie die Schalter. Das Formular
sollte wie hier gezeigt aussehen:
.. image:: images/DisplayFormButtonScreenShot.png
Die Erstellung des Bearbeiten-Formulars
---------------------------------------
Der Vorteil der automatisch generierten Formulare sollte nun sichtbar werden.
Der abschließende Schritt, für die Erstellung des Formulars, ist der einfachste
von allen. Hier ist der notwendige Code:
.. sourcecode:: python
class ContactEditForm(form.EditForm):
"""A simple edit form for contacts."""
fields = field.Fields(interfaces.IContact)
mit der dazugehörigen zcml-Konfiguration:
.. sourcecode:: xml
Probieren Sie nun das Bearbeiten-Formular aus, nachdem Sie das
Anzeige-Formular aufgerufen haben. Beachten Sie den "Apply"-Schalter
zum Abschicken der Daten. Als Anwort erhalten Sie eine Statusmeldung
für den Erfolg oder Misserfolg der Speicheraktion. An dieser Stelle
wollen wir aber zurück zum Hinzufügen-Formular, deshalb fügen wir
noch einen "Done"-Schalter mit dem folgenden Code zur
``ContactEditForm``-Klasse hinzu:
.. sourcecode:: python
@button.buttonAndHandler(u'Done', name='done')
def handleDone(self, action):
self.request.response.redirect(absoluteURL(self.context, self.request))
Aber halt! Sobald man einen eigenen Schalter erstellt, werden die
von ``form.EditForm`` Klasse definierten Schalter überschrieben.
Um dies zu verhindern, wird die ``form.EditForm`` -Klasse *erweitert*
indem Sie ``form.extends(form.EditForm)`` direkt hinter der
``ContactEditForm``-Klasse deklarieren. Sie sollten nun ein
Bearbeiten-Formular erhalten, das wie folgt aussieht:
.. image:: images/EditFormScreenShot.png
Eine Startseite für die Anwendung
=================================
Als vorläufigen Abschluss, erstellen Sie für die Anwendung eine
Startseite, bevor Sie sich weiter mit dem "Skinning" beschäftigen.
Die nächste Seite soll eine Startseite werden, die eine Möglichkeit
zum Hinzufügen eines neuen Kontakts sowie jeweils einen Link zu den
vorhandenen Kontakten enthält. Dies alles ist mit einem einfachen
page template ``zcontact/browser/frontpage.pt`` möglich:
.. sourcecode:: xml
Welcome to ZContact
Please tell me what you would like to do:
Dieses page template wird über zcml für das ``IRootFolder``-Interface
registriert.
Fügen Sie in ``zcontact/browser/configure.zcml`` folgende Zeilen hinzu:
.. sourcecode:: xml
Nach dem Server-Neustart kann die Startseite wie folgt getestet werden:
http://localhost:8080/++skin++ZContact/ und Sie sollten in etwa die folgnede
Ansicht erhalten:
.. image:: images/FrontPageScreenShot.png
Mit ein paar Formularen und einer funktionierenden Anwendung können Sie sich nun
andere z3c.*-Komponenten ansehen.