Samstag, 1. August 2015

WTForms: Daten für SelectField nachträglich einfügen

WTForms gehört in der Python-Welt zu den populäreren Modulen zum Generieren und Validieren von HTML-Formularen für Webanwendungen.

Hier im Blogeintrag geht es aber nicht um WTForms im gesamten - die offizielle Doku ist ja so wie so recht ausführlich - sondern "nur" um das nachträgliche Hinzufügen von Auswahlmöglichkeiten für ein SelectField (=Auswahlfeld). Wobei das im folgenden gezeigt auch für das RadioField gilt.

Vorab noch: Alle folgenden Beispiele sind mit WTForms 2.0.2 unter Python 3.4 getestet. Der Einfachheit wird alles im interaktiven Python-Interpreter auf der Kommandozeile durchgespielt. Das Vorgehen in realen Webanwendungen ist aber identisch.

Eine einfaches Formular mit einem SelectField sieht z.B. so aus:

>>> from wtforms import Form, StringField, SelectField
>>> class TestForm(Form):
...     name = StringField('name')
...     gender = SelectField('gender',

            choices=[('m', 'male'),
                     ('f', 'female')])
>>>

Zuerst werden die benötigten Klassen importiert, dann wird eine eigene Formularklasse Namens TestForm definiert, welche die zwei Felder name und gender hat. gender ist dabei ein  Auswahlfeld.

Die Auswahlmöglichkeiten werden über das choices-Attribute festgelegt. Diesem wird eine Liste von Tupeln zuwiesen, wobei das erste Element der value der Option ist und das zweite Element der angezeigte Text.

Erzeugt man nun eine neue Instanz der Klasse

>>> my_form = TestForm()

kann man sich das HTML-Markup, welche das Formular erzeugt, auch direkt auf der Kommandozeile anzeigen lassen, z.B. für gender:

>>> str(my_form.gender)
'<select id="gender" name="gender"><option value="m">male</option><option value="f">female</option></select>'

>>>

Jetzt gibt es aber natürlich auch genug Anwendungsfälle, wo man in einer Webanwendung ein Auswahlfeld dynamisch befüllen will und somit die Werte bei der Definition der Klasse noch nicht kennt.

Auch das ist natürlich mit WTForms problemlos möglich und zwar indem man das choices-Attribut erst später mit Daten füttert. Dies wird im folgenden Beispiel gezeigt:

>>> class TestForm2(Form):
...     name = StringField('name')
...     fav_color = SelectField('favourite color')
...
>>> form2 = TestForm2()


Wie zu sehen ist, wird für das SelectField kein Wert für choices übergeben. Folglich kann auch kein HTML generiert werden, stattdessen wird ein Fehler geworfen:

>>> str(form2.fav_color)
Traceback (most recent call last):
...
TypeError: 'NoneType' object is not iterable
>>>


Versorgt man das choices-Attribute mit Daten, dann funktioniert alles:

>>> my_choices = [('w', 'white'),
                  ('r', 'red'),
                  ('b', 'blue')]
>>> form2.fav_color.choices = my_choices
>>> str(form2.fav_color)
'<select id="fav_color" name="fav_color"><option value="w">white</option><option value="r">red</option><option value="b">blue</option></select>'

>>>

In einer realen Anwendung würde die Liste natürlich nicht hard-coded im Quelltext stehen, sondern z.B. aus einer Datenbankabfrage kommen.

Wie andere Field-Klassen auch kennt SelectField ebenfalls einen Default-Wert, was sich im HTML-Formular dann so darstellt, dass diese Option vorausgewählt ist.

Wollte man z.B. im ersten Beispiel female als Default-Wert haben, dann müsste die Zeile im Code so geändert werden:

gender = SelectField('gender',
            choices=[('m', 'male'),
                     ('f', 'female')],
            default='f')

Will man beim 2. Beispiel  die Farbe weiß als Default-Wert setzen, dann muss man das default-Attribute ebenfalls mit einem Wert versorgen:

>>> form2.fav_color.default='w'
>>> str(form2.fav_color)
'<select id="fav_color" name="fav_color"><option value="w">white</option><option value="r">red</option><option value="b">blue</option></select>'
>>> form2.fav_color.default
'w'
>>>


Das alleine reicht aber nicht, wie im obigen Code zu sehen ist, hat sich am generierten HTML aber nichts geändert, obwohl der Default-Wert korrekt gesetzt ist.

Damit das HTML wie gewünscht ist, muss ein weiterer Schritt durchgeführt werden, und zwar der Aufruf der process-Methode der Klasse: :

>>> form2.process()
>>> str(form2.fav_color)
'<select id="fav_color" name="fav_color"><option selected value="w">white</option><option value="r">red</option><option value="b">blue</option></select>'
>>>


So ist auch der Default-Wert korrekt gesetzt.

Keine Kommentare:

Kommentar veröffentlichen