Information
Diese Aufgabe basiert auf keinem CS50 Practice Problem.
Disclaimer: Diese Aufgabe wurde nicht vom Lehrstuhl herausgegeben und kann Fehler enthalten. Sie dient, wie das gesamte Material von inf-lab.dev, lediglich zu Übungszwecken!
Kontaktformulare sind ein zentraler Bestandteil fast jeder Website. Sie bieten eine einfache und strukturierte Möglichkeit, Informationen zwischen Nutzern und Website-Betreibern auszutauschen, sei es für Support-Anfragen, Feedback oder Geschäftsanfragen.
In dieser Aufgabe möchten wir unser eigenes Kontaktformular bauen, sodass auch wir kontaktiert werden könnten! Hierzu verwenden wir Flask auf der Server-Seite und Bootstrap auf der Client-Seite, dass unsere Website auch gut aussieht.
Um das Aufgabenmeterial herunterzuladen, gib folgenden Befehl in ein neues Terminal in deinem Codespace ein:
wget -O - https://inf-lab.dev/contact/material/lab-contact.zip.sh | bash
Wie immer, wenn wir einen vorgegebenen Code haben, sollten wir diesen zuerst verstehen. Betrachte deshalb den gegebenen Code in app.py
, error.html
und layout.html
und beantworte folgende Fragen.
In der Datei app.py
ist eine Klasse ContactRequest
gegeben. Diese hat ein Attribut id
welches automatisch einen eindeutigen String enthält um diese Anfrage zu identifizieren[1].
id
Attribut zugegriffen werden?In der Datei error.html
ist ein Jinja2 Template gegeben, welches eine Fehlermeldung ausgibt.
<html>
oder <body>
Element?`In der Datei layout.html
ist ein weiteres Template gegeben. Dieses stellt die Struktur unserer Seite dar.
Ändere abschließend das Template layout.html
so ab, dass es mittels extends
erweitert werden kann um somit Inhalt einzufügen. Füge hierzu einen Block ein und nenne diesen body
, sodass das error.html
Template auch funktioniert.
Unsere HTML-Dateien verwenden hier Jinja2 Templates, welche von Flask ausgewertet werden können. Somit können wir recht einfach HTML dynamisch aus unseren Python Daten erstellen.
Im folgenden findet sich ein einfaches Beispiel, welches in abgewandelter Version auch in der Dokumentation gefunden werden kann. open_todos
und todos
sind hierbei Variablen, die dem Template explizit übergeben wurden.
<div>
<h1>My ToDos</h1>
Open todos: {{ open_todos }}
<ul id="list">
{% for todo in todos %}
<li>{{ todo }}</li>
{% endfor %}
</ul>
{# some comment #}
</div>
Warnung
Viele unserer Implementierungen in dieser Aufgabe sind anfällig für diverse Angriffe und sollten in der Praxis anders umgesetzt werden. Für die Zwecke von Inf-Einf reicht das aber aus.
Da wir nun die Grundstruktur unserer Website verstanden und erweitert haben, ist es an der Zeit das eigentliche Kontaktformular zu erstellen.
Erstelle hierzu eine neue Datei index.html
an der passenden Stelle. Erweitere in dieser das layout.html
und erstelle ein Formular. Das Formular soll, sobald es abgesendet wird, eine POST
Anfrage an den /submit
Pfad (welche wir später in Python definieren werden) senden.
Im Formular muss der Benutzer die Möglichkeit haben, seinen Namen name
, seine E-Mail email
, einen Betreff subject
und eine eigentliche Nachricht text
einzugeben. Wähle für jedes dieser Felder ein sinnvolles HTML-Element mit gut gewählten Attributen und achte darauf, dass der Benutzer immer weiß in welchem Feld er sich gerade befindet. Vergiss nicht, auch einen Send
Button hinzuzufügen.
Füge schließlich noch einen Button hinzu, um das Formular abzusenden.
Dass unser Kontaktformular auch gut aussieht, soll Bootstrap für das Styling verwendet werden. Achte deshalb darauf, allen Elementen passende Bootstrap Klassen zuzuweisen.
Das fertige Formular, inklusive des Layouts, könnte wie im folgenden Bild aussehen.
Folgender Code ist ein leicht abgeändertes Beispiel aus der Bootstrap Dokumentation zu Formularen.
<label
for="title"
class="form-label"
>
Titel
</label>
<input
type="text"
class="form-control"
id="title"
name="title"
/>
Da wir nun unsere index.html
Datei fertig geschrieben haben, sollten wir diese auch über Flask bereitstellen. Erstelle hierzu in der Datei app.py
deinen Flask Server.
Beginne damit, deine app
zu erstellen und definiere schließlich eine route
für den /
Pfad. Der /
Pfad soll GET
Anfragen akzeptieren und das Template index.html
ausgeben.
Wenn du alles richtig gemacht hast, solltest du nun deinen Server starten können, und direkt mit deinem Formular begrüßt werden!
Um Templates auszugeben (zu "rendern"), kann die Funktion render_template
verwendet werden. Folgender Beispielcode würde beispielsweise das Template todos.html
ausgeben. Nach dem Namen des Templates können optional weitere benannte Parameter folgen um einzelne Variablen im Template verfügbar zu machen.
from flask import render_template
render_template("todos.html", count=4)
Wichtig: Vergiss nicht, den Rückgabewert von render_template
auch zu return
en, sonst wird das Template zwar ausgeführt aber nicht ausgegeben!
Nun haben wir schon einen Flask Server, welcher unsere index.html
mit Formular darstellt. Nun möchten wir noch die Eingaben des Formulars verarbeiten.
Erstelle hierzu eine weitere Flask route
für den Pfad, welchen wir in Teilaufgabe 2 im <form>
angegeben haben. Diese URL soll nur POST
Anfragen akzeptieren. Lies dann in dieser Funktion die per Formular übermittelten Parameter aus und prüfe, ob diese überhaupt angegeben wurden. Sollte ein Parameter fehlen, kannst du das Template error.html
verwenden um eine Fehlermeldung auszugeben.
Im folgenden findest du ein Beispielbild, wie Fehlermeldungen mit dem error.html
Template aussehen können.
Sollten alle Daten korrekt übermittelt worden sein, soll aus diesen eine neue ContactRequest
erstellt werden. Speichere diese ContactRequest
schließlich in einer Datenstruktur deiner Wahl. Beachte hierbei, dass wir die ContactRequest
später anhand ihrer id
finden wollen. Gib im Erfolgsfall zusätzlich einen Success!
Text zurück.
Information
Da wir die ContactRequest
s nur in einer Variable speichern bedeutet das, dass Kontaktanfragen nur gespeichert werden, solange der Server auch läuft. Dies wäre in der Praxis eher unpraktisch. Für die Zwecke dieser Übung ist das aber vollkommen ausreichend.
Achte darauf, dass deine Funktionen auch korrekte HTTP-Antwortstatuscodes verwenden.
Um den Wert eines übermittelten Formularfeldes auszulesen, kann die request.form.get(name)
Funktion verwendet werden. Im folgenden findest du ein informelles Beispiel, um das im HTML dargestellte Feld auszulesen.
<label for="task_field">Task</label>
<input
name="task"
id="task_field"
/>
from flask import request
task = request.form.get("task")
Wichtig: Dass du ein Feld im HTML-Formular erstellt hast, bedeutet nicht zwingend, dass es auch an den Server übermittelt wird. Prüfe deshalb immer erst ob das Feld nicht None
ist.
Nun können Benutzer uns schon ihre Kontaktanfragen zusenden und diese werden auch gespeichert. Jedoch sollten wir Benutzern auch die Möglichkeit geben, ihre bereits gesendeten Anfragen anzusehen.
Erstelle deshalb eine neue Route /view
, welche nur GET
Anfragen akteptiert. Diese soll dann die als Query-Parameter übergebene id
auslesen, und überprüfen, ob es eine ContactRequest
mit dieser id
gibt. Ist dies der Fall, soll das Template view.html
ausgegeben werden, welches wir gleich erstellen werden. Sollte keine ContactRequest
für die übergebene id
existieren, kann wieder das Template error.html
verwendet werden.
Erstelle deshalb auch noch ein weiteres Template view.html
im passenden Verzeichnis, welches eine ContactRequest
übergeben bekommt. Dieses Template soll dann alle Eingaben des Benutzers auflisten.
Im folgenden ist ein Bild gegeben, wie diese Auflistung aussehen könnte.
Damit dem Benutzer die id
nach der Erstellung auch bekannt ist, müssen wir noch unsere /submit
Route anpassen. Sorge hierbei dafür, dass statt dem Text Success!
eine Weiterleitung auf die passende /view?id=<id>
Seite erfolgt. Hierbei muss <id>
natürlich durch die id
des ContactRequest
s ersetzt werden.
Achte auch hier darauf, dass deine Funktion korrekte HTTP-Antwortstatuscodes verwendet.
Query oder auch Get-Parameter sind Eingaben, welche über die URL übermittelt werden. Diese folgen nach einem ?
in der URL.
Beispielsweise hat die URL https://www.google.com/search?q=Hallo
den Query-Parameter q
mit dem Wert Hallo
.
Diese Parameter können in Flask in etwa wie im folgenden Beispiel ausgelesen werden.
from flask import request
q = request.args.get("q")
Wichtig: Vergiss nicht zu prüfen ob das Feld nicht None
ist!
Um den Benutzer auf eine andere Seite weiterzuleiten, kann die redirect(url)
Funktion benutzt werden. Im folgenden informellen Beispiel wird der Benutzer auf die /todos
Seite weitergeleitet.
from flask import redirect
redirect("/todos")
Wichtig: Vergiss nicht den Rückgabewert von redirect
auch zu return
en, sonst wird die Weiterleitung nicht ausgeführt!
Tipp
Natürlich kannst du redirect
auch f-Strings übergeben!
Benutze flask run
in einem Terminal, während du dich im Verzeichnis lab-contact
befindest, um deinen Flask-Server zu starten, der die Webseite bereitstellt.
Für diese Aufgabe gibt es kein check50
, da die Implementierungen sehr unterschiedlich ausfallen können.
Leider unterstützt style50
keine HTML-Dateien. Daher liegt es an dir, deine HTML-Tags sauber einzurücken und auszurichten. Deine app.py
Datei kannst du jedoch mit folgendem Befehl überprüfen.
style50 app.py