[Django] Formular in zwei Schritten, wenn neuer Datensatz

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
Benutzeravatar
filchos
User
Beiträge: 35
Registriert: Dienstag 2. Juni 2009, 10:48
Wohnort: München
Kontaktdaten:

Hallo,

um ein bestimmtes Model zu editieren benutze ich eine Formularklasse, die von forms.ModelForm erbt.

Der einfache Fall: Einen bestehenden Datensatz editieren.

Bis hier ist noch alles Standard.

Der komplexe Fall: Einen neuen Datensatz anlegen.

Das Anlegen soll in zwei Schritten geschehen:

- Schritt 1:

Erst wird eine ID eingegeben und dann [weiter] geklickt. Im Fehlerfall (not form.is_valid()) bleibt man auf der Seite und es erscheint eine Fehlermeldung. Sonst geht es weiter mit …

- Schritt 2:

Auf einer zweiten Formularseite ist die ID nicht mehr editierbar. Die weiteren, jetzt neu erscheinenden Felder sind teilweise vorbelegt.

Jetzt kommt die Stelle, mit der ich ein Problem habe:

Wenn ich das Formular beim Erzeugen mit Werten vorbelege, so wird es durch Django automatisch validiert. Falls die Validierung fehlschlägt – es könnte z.B. ein Pflichtfeld noch nicht vorausgefüllt sein – so wird dem User dies als Fehler mitgeteilt. Dieses Verhalten möchte ich vermeiden, da die Werte vom System und nicht vom User eingetragen werden. Das Formular soll erst dann validiert werden, wenn der User auf [Speichern] klickt.

Wie macht man so etwas?

Zusatzfrage: Verwendet man für jeden der beiden Schritte eine eigene Unterklasse von forms.ModelForm oder nimmt man immer die gleiche und ändert das Verhalten?

Schöne Grüße,
Olaf
Benutzeravatar
filchos
User
Beiträge: 35
Registriert: Dienstag 2. Juni 2009, 10:48
Wohnort: München
Kontaktdaten:

Nachtrag:

ich habe als erste Lösung ein Flag im View gesetzt, welches im Template bestimmt, ob Validierungsfehler angezeigt werden oder nicht.

Nicht besonders schön, aber erst einmal funktionabel. Refactoring kommt dann später.

Grüße,
Olaf
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

filchos hat geschrieben: Wenn ich das Formular beim Erzeugen mit Werten vorbelege, so wird es durch Django automatisch validiert. Falls die Validierung fehlschlägt – es könnte z.B. ein Pflichtfeld noch nicht vorausgefüllt sein – so wird dem User dies als Fehler mitgeteilt. Dieses Verhalten möchte ich vermeiden, da die Werte vom System und nicht vom User eingetragen werden. Das Formular soll erst dann validiert werden, wenn der User auf [Speichern] klickt.
Dies ist definitiv nicht das Verhalten von Django, wie belegst du die Werte vor? mit initial so wie du sollst? Zeig mal den Code
Benutzeravatar
filchos
User
Beiträge: 35
Registriert: Dienstag 2. Juni 2009, 10:48
Wohnort: München
Kontaktdaten:

Hallo apollo13,

ich musste den Viewcode kürzen, ich hoffe, er ist durch die Kommentare dennoch noch verständlich:

Code: Alles auswählen

def edit(request, id):
    
    # get optional instance
    # if new: get step (1 or 2)
    if id == 'new':
        params = {}
        step   = int(request.POST.get('step', 1))
    else:
        params = {'instance': MyModel.objects.get(id=id)}
        step   = None

    # assign the form class
    if step == 1:
        # form for the 1st step containing only a part of the fields
        form_object = MyModelStep1Form
    else:
        # form for the 2nd step or editing containing all fields
        form_object = MyModelForm

    # get the matching form object
    if request.method == 'POST':
        form = form_object(request.POST, **params)
    elif step == 1:
        values = {
            'key1': value1,
            'key2': a_function_returning_value2(),
        }
        form = form_object(values, {})
    else:
        form = form_object(**params)

    ignore_errors = False
    if request.method == 'POST':

        # validate and try to save
        if form.is_valid():
            
            if step == 1:
                step += 1
                # create another form with values from POST and a new calculated value3
                values = {
                    'key1': request.POST.get('key1', None),
                    'key2': request.POST.get('key2', None),
                    'key3': a_function_returning_value3(),
                }
                form = MyModelForm(values)
                ignore_errors = True
                
            else:
                form.save()
                return my_own_redirect_function()
        
    else:
        ignore_errors = True

    # display

    return my_own_render_function(ignore_errors)
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Zeile 28, du musst das Form mit initial initialisieren…
Benutzeravatar
filchos
User
Beiträge: 35
Registriert: Dienstag 2. Juni 2009, 10:48
Wohnort: München
Kontaktdaten:

Hallo apollo13,

vielen Dank, wieder etwas über Django gelernt. Das Framework gefällt mir sehr gut, aber die Doku finde ich manchmal etwas unübersichtlich.

Grüße,
Olaf

Fürs Archiv:

Dokumentation dazu: http://docs.djangoproject.com/en/dev/re ... rm.initial
Benutzeravatar
filchos
User
Beiträge: 35
Registriert: Dienstag 2. Juni 2009, 10:48
Wohnort: München
Kontaktdaten:

Folgefrage:

wie kann ich im Template nur den Wert eines Feldes anzeigen lassen?

Mein Versuch (Ausschnitt):

views.py:

Code: Alles auswählen

# […]
values = {'foo': get_bar(), }
form = form_object(initial=values)
# […]
template:

Code: Alles auswählen

<p>{{form.foo.data}}</p>
Leider erhalte ich als Ausgabe nicht den durch get_bar() errechneten Wert, sondern None.

Schöne Grüße,
Olaf
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

Gegenfrage, wofür brauchst du das?
Benutzeravatar
filchos
User
Beiträge: 35
Registriert: Dienstag 2. Juni 2009, 10:48
Wohnort: München
Kontaktdaten:

apollo13 hat geschrieben:Gegenfrage, wofür brauchst du das?
Die Frage ist natürlich berechtigt.

Ich brauche diesen Wert, um ihn in einer von mir gewünschten Weise im Formular im Template anzeigen zu können.

Ich habe das zur Zeit gelöst, in dem ich ein Widget „ReadOnly“ geschrieben habe und dafür benutze. Das ist vermutlich auch der bessere Weg.

Die Frage bleibt daher mehr aus theoretischem Interesse bestehen.

Zusatzfrage: Kann ich im View nachträglich das verwendete Widget für ein Formularfeld ändern?

Grüße,
Olaf
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

filchos hat geschrieben: Ich habe das zur Zeit gelöst, in dem ich ein Widget „ReadOnly“ geschrieben habe und dafür benutze. Das ist vermutlich auch der bessere Weg.
wenn du die attrs auf readonly setzt heißt das nicht, dass der user das nicht verändern kann, nur zur info ;) Wenn du nur den initial Wert anzeigen willst sollte form.field.initial gehen
Zusatzfrage: Kann ich im View nachträglich das verwendete Widget für ein Formularfeld ändern?
form.fields['field_name'].widget = MyNewWidget, mit Vorsicht genießen…
Benutzeravatar
filchos
User
Beiträge: 35
Registriert: Dienstag 2. Juni 2009, 10:48
Wohnort: München
Kontaktdaten:

apollo13 hat geschrieben:wenn du die attrs auf readonly setzt heißt das nicht, dass der user das nicht verändern kann, nur zur info ;)
Das ist klar. Der Wert wird bei meinem Widget als Hidden-Field im Formular übertragen. Natürlich wird der Wert validiert, denn der ist ja, wie jede Usereingabe – z.B. mit Firebug – schnell geändert.

Grüße,
Olaf
Antworten