ModelsForms mit Choices validiert nicht korrekt

22. Juli 2008 19:15:42 Uhr

Um es vorweg zu nehmen, dies ist die Beschreibung eines Bugs in Django. Ja, dafür gibt es schon ein Ticket und nein, es ist derzeit noch nicht im Trunk gefixt.

Oki, wer bis hierhin durchgehalten hat, weiter gehts: Smiley:  :-)

Angenommen wir haben dieses simple Model, die Werte des Testfeldes bestehen aus den vorher definierten Choices.

from django.db import models

CHOICES = (
    (u'Bube', u'Bube'),
    (u'Dame', u'Dame'),
    (u'Koenig', u'Koenig'),
    (u'Ass', u'Ass'),
)

# Das Model
class TestModel(models.Model):
    testfeld = models.CharField(max_length=30, choices=CHOICES)

Um ein simples Formular daraus zu erstellen ist ModelForms ideal:

from testprojekt.models import TestModel
from django import forms # Django 1.0 Alpha wohooo!

class TestForm(forms.ModelForm):
    class Meta:
        model = TestModel

Einfacher gehts kaum, mit den paar Zeilen hat man schon ein komplettes Formular parat. Aber dass wußtest du sicher schon, also weiter. Basteln wir uns eine einfache Ausgabe:

>>> f = TestForm()
>>> f.as_ul()

# Beispielausgabe
<select name="testfeld" id="id_testfeld">
    <option value="Bube">Bube</option>
    <option value="Dame">Dame</option>
    <option value="Koenig">Koenig</option>
    <option value="Ass">Ass</option>
</select>

Alles korrekt bis hier hin. Angenommen das Formular wird nun auf der Webseite angezeigt, der User wählt "Bube" und schickt das Formular ab:

>>> f = TestForm(data={'testfeld': 'Bube'})
>>> f.as_ul()

<select name="testfeld" id="id_testfeld">
    <option value="Bube" selected="selected">Bube</option>
    <option value="Dame">Dame</option>
    <option value="Koenig">Koenig</option>
    <option value="Ass">Ass</option>
</select>

>>> f.is_valid()
True

Das Formular ist valide, weil "Bube" ja eine korrekte Auswahl für das Feld 'testfeld' ist. Aber zur Erinnerung: SELECT-Felder in HTML sind auch nur stinknormale Datenfelder und der Browser prüft nicht, ob der abgesendete Wert auch des Feldes auch vorher in den SELECT-Options definiert wurde. Ändern wir also mal die Werte der Formulardaten:

>>> f = TestForm(data={'testfeld': 'Boeses Maedchen'})
>>> f.as_ul()

<select name="testfeld" id="id_testfeld">
    <option value="Bube">Bube</option>
    <option value="Dame">Dame</option>
    <option value="Koenig">Koenig</option>
    <option value="Ass">Ass</option>
</select>

>>> f.is_valid()
True

Wem das nicht schlimm erscheint: Diese Daten speichert das Formular leider auch in die Datenbank, das Model selbst validiert also nicht noch einmal nach.

>>> objekt = f.save()
>>> objekt.testfeld
u'Boeses Maedchen'

Ganz fatal in meinen Augen. Zur Erinnerung, eigentlich sollte das testfeld im Model nur die Daten Bube, Dame, Koenig und Ass annehmen.

Wie kann man eigentlich "fremde" Daten in ein Select-Feld schreiben? Nichts einfacher als das, die bekannte Firefox-Extension "Web Developer Toolbar" hat eine eigene Funktion dafür:

Mein derzeitiger Lösungsvorschlag sieht so aus, dass ich einfach mit einer clean-Methode auf das Vorhandensein der Eingabe in den Choices prüfe:

from testprojekt.models import TestModel, CHOICES
from django import forms

class TestForm(forms.ModelForm):

    def clean_testfeld(self):
        if not self.cleaned_data['testfeld'] in dict(CHOICES):
            raise forms.ValidationError(u'%s is not a valid choice' % (self.cleaned_data['testfeld']))            
        return self.cleaned_data['testfeld']

    class Meta:
        model = TestModel

Dann klappts auch wie gewünscht.

>>> f = TestForm(data={'testfeld': 'Boeses Maedchen'})
>>> print f.is_valid()
False
>>> f.as_ul()
errorlist: "Boeses Maedchen is not a valid choice"
<select name="testfeld" id="id_testfeld">
    <option value="Bube">Bube</option>
    <option value="Dame">Dame</option>
    <option value="Koenig">Koenig</option>
    <option value="Ass">Ass</option>
</select

Durch das derzeitge Release von Django 1.0 Alpha gehe ich aber mal davon aus, dass dieser Bug in nächster Zeit gefixt wird. Ein Patch exisitiert ja schon.


Dieser Eintrag wurde am 22. Juli 2008 um 19:15 Uhr veröffentlicht.

© 2001–2007 Martin Mahner | www.mahner.org