Programming lesson
Flask-Website mit SQLite-Datenbank: Ein Backwettbewerb-Projekt für Anfänger
Lerne, wie du mit Flask, Python und SQLite eine dynamische Website für einen Backwettbewerb erstellst – inklusive Formularvalidierung und Datenbankabfragen.
Einführung in Flask und SQLite
In diesem Tutorial zeige ich dir, wie du mit Flask, Python und SQLite eine voll funktionsfähige Website für einen Backwettbewerb erstellst. Das Projekt basiert auf typischen Aufgaben aus der Hochschullehre (z.B. COP4521) und eignet sich perfekt, um relationale Datenbanken und Webentwicklung praxisnah zu lernen. Aktuell im Mai 2026 sind Web-Apps mit Datenbankanbindung gefragter denn je – ob für Gaming-Communitys, Eventmanagement oder KI-gestützte Tools. Ein Backwettbewerb ist ein ideales Beispiel, weil er einfache CRUD-Operationen (Create, Read, Update, Delete) abdeckt.
Projektstruktur und Setup
Zunächst benötigst du eine grundlegende Ordnerstruktur für deine Flask-App. Erstelle ein Verzeichnis backwettbewerb mit folgenden Dateien:
app.py– Hauptdatei mit Flask-Routendatabase.py– SQLite-Datenbankinitialisierungtemplates/– Ordner für HTML-Vorlagenstatic/– optional für CSS/JS
Installiere Flask mit pip install flask. Wir verwenden SQLite3, das in Python integriert ist, also kein zusätzlicher Treiber nötig.
Datenbankmodell erstellen
Unsere Datenbank enthält zwei Tabellen: baking_contest_people für Teilnehmer und baking_contest_entries für ihre Backwerke. Die SQL-Definition könnte so aussehen:
CREATE TABLE baking_contest_people (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER NOT NULL,
phone_number TEXT NOT NULL,
security_level INTEGER NOT NULL CHECK(security_level BETWEEN 1 AND 3),
login_password TEXT NOT NULL
);
CREATE TABLE baking_contest_entries (
entry_id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
name_of_baking_item TEXT NOT NULL,
num_excellent_votes INTEGER DEFAULT 0,
num_ok_votes INTEGER DEFAULT 0,
num_bad_votes INTEGER DEFAULT 0,
FOREIGN KEY(user_id) REFERENCES baking_contest_people(id)
);Die Tabelle baking_contest_people speichert Name, Alter, Telefonnummer, Sicherheitsstufe (1-3) und Passwort – genau wie in der Aufgabenstellung gefordert. Die entries-Tabelle erfasst die Bewertungen: ausgezeichnet, okay und schlecht. Ein Fremdschlüssel verknüpft jeden Eintrag mit einem Benutzer.
Flask-App einrichten
In app.py importieren wir Flask, render_template, request und redirect. Wir definieren die Datenbankverbindung mit SQLite. Ein Beispiel für die Home-Route:
from flask import Flask, render_template, request, redirect, url_for
import sqlite3
app = Flask(__name__)
def get_db():
conn = sqlite3.connect('baking_contest.db')
conn.row_factory = sqlite3.Row
return conn
@app.route('/')
def home():
return render_template('home.html')Die Homepage zeigt drei Links: „Neuen Teilnehmer hinzufügen“, „Teilnehmer auflisten“ und „Ergebnisse anzeigen“. Das HTML-Template verwendet einfache semantische Tags:
<h2>Backwettbewerb-Verwaltung</h2>
<p>Willkommen beim Backwettbewerb! Wähle eine Aktion:</p>
<ul>
<li><a href="{{ url_for('add_user') }}">Neuen Teilnehmer hinzufügen</a></li>
<li><a href="{{ url_for('list_users') }}">Teilnehmer auflisten</a></li>
<li><a href="{{ url_for('list_results') }}">Ergebnisse anzeigen</a></li>
</ul>Formular für neue Teilnehmer
Die Seite „Add a New Baking Contest User“ enthält ein Formular mit Feldern für Name, Alter, Telefonnummer, Sicherheitsstufe und Passwort. Wichtig: Die Validierung erfolgt serverseitig. Die Route /add_user verarbeitet GET- und POST-Anfragen:
@app.route('/add_user', methods=['GET', 'POST'])
def add_user():
if request.method == 'POST':
name = request.form['name'].strip()
age = request.form['age']
phone = request.form['phone'].strip()
security = request.form['security']
password = request.form['password'].strip()
errors = []
# Validierung
if not name:
errors.append('Name darf nicht leer sein.')
try:
age_int = int(age)
if age_int <= 0 or age_int >= 121:
errors.append('Alter muss zwischen 1 und 120 liegen.')
except ValueError:
errors.append('Alter muss eine ganze Zahl sein.')
if not phone:
errors.append('Telefonnummer darf nicht leer sein.')
try:
sec_int = int(security)
if sec_int < 1 or sec_int > 3:
errors.append('Sicherheitsstufe muss 1, 2 oder 3 sein.')
except ValueError:
errors.append('Sicherheitsstufe muss eine Zahl sein.')
if not password:
errors.append('Passwort darf nicht leer sein.')
if errors:
msg = 'Fehler: ' + '; '.join(errors)
return render_template('result.html', msg=msg)
else:
conn = get_db()
conn.execute('INSERT INTO baking_contest_people (name, age, phone_number, security_level, login_password) VALUES (?,?,?,?,?)',
(name, age_int, phone, sec_int, password))
conn.commit()
conn.close()
msg = 'Teilnehmer erfolgreich hinzugefügt!'
return render_template('result.html', msg=msg)
return render_template('add_user.html')Die Validierung prüft, ob das Alter eine ganze Zahl zwischen 1 und 120 ist, die Sicherheitsstufe numerisch zwischen 1 und 3 liegt und alle Felder ausgefüllt sind. Bei Fehlern wird eine aussagekräftige Meldung an die Ergebnisseite übergeben.
Teilnehmer auflisten
Die Route /list_users holt alle Datensätze aus der Tabelle und zeigt sie in einer HTML-Tabelle an:
@app.route('/list_users')
def list_users():
conn = get_db()
users = conn.execute('SELECT * FROM baking_contest_people').fetchall()
conn.close()
return render_template('list_users.html', users=users)Das Template listet die Spalten Name, Alter, Telefonnummer, Sicherheitsstufe und Passwort auf. Ein Link führt zurück zur Startseite.
Ergebnisse anzeigen
Die Ergebnisseite zeigt alle Einträge aus der baking_contest_entries-Tabelle an. Ähnlich wie bei der Benutzerliste:
@app.route('/list_results')
def list_results():
conn = get_db()
entries = conn.execute('SELECT * FROM baking_contest_entries').fetchall()
conn.close()
return render_template('list_results.html', entries=entries)Die Tabelle enthält Entry ID, User ID, Name des Backwerks, Anzahl ausgezeichneter, okay und schlechter Stimmen. Auch hier gibt es einen Zurück-Link.
Ergebnisseite mit dynamischer Nachricht
Die result.html-Seite empfängt eine Variable msg und zeigt sie an. Sie enthält außerdem einen Link zurück zur Startseite:
<h2>Ergebnis</h2>
<p>{{ msg }}</p>
<a href="{{ url_for('home') }}">Zurück zur Startseite</a>Testen und Fehlerbehandlung
Starte die App mit python app.py und rufe http://127.0.0.1:5000 auf. Teste alle Seiten: Füge einen neuen Teilnehmer hinzu, lass Felder leer oder gib ungültige Werte ein (z.B. Alter 200). Prüfe, ob die Fehlermeldungen korrekt angezeigt werden. Die Liste der Teilnehmer sollte den neuen Eintrag enthalten. Für die Ergebnisse benötigst du initiale Testdaten in der baking_contest_entries-Tabelle – füge sie manuell über SQLite oder ein Seed-Skript hinzu.
Erweiterungsmöglichkeiten
Dieses Grundgerüst lässt sich leicht erweitern: Du könntest eine Suchfunktion einbauen, die Ergebnisse nach Kategorien filtern oder ein Login-System mit Session-Verwaltung hinzufügen. Auch das Styling mit CSS ist schnell umgesetzt. In der heutigen Zeit, in der viele Events wie Gaming-Turniere oder KI-Wettbewerbe auf ähnlichen Systemen basieren, sind diese Kenntnisse sehr wertvoll. Wenn du zum Beispiel eine Leaderboard für ein E-Sports-Event bauen möchtest, kannst du das gleiche Prinzip anwenden – nur mit anderen Tabellen.
Häufige Fehler vermeiden
- Datenbankpfad: Stelle sicher, dass die SQLite-Datenbank im selben Verzeichnis wie
app.pyliegt oder gib den absoluten Pfad an. - Formular-Methoden: Verwende
POSTfür das Absenden von Daten,GETnur zum Abrufen. - Validierung: Führe alle Überprüfungen serverseitig durch, auch wenn du clientseitig JavaScript verwendest.
- Session-Handling: Wenn du Passwörter speicherst, denke an Hashing (z.B. mit
werkzeug.security), aber das ist hier optional.
Fazit
Du hast nun eine funktionierende Flask-Website mit SQLite-Datenbank erstellt, die alle geforderten Seiten enthält. Dieses Projekt ist ein hervorragender Einstieg in die Webentwicklung mit Python und relationalen Datenbanken. Die Struktur lässt sich auf viele andere Anwendungen übertragen – von der Verwaltung von Sportergebnissen bis hin zu KI-Modell-Bewertungen. Viel Erfolg bei deinem Backwettbewerb!