13. Unterrichtsblock

Kursinhalte
- Was ist Flask?
- Installation von Flask
- Bedeutung von JSON für Flask
Einführung in Flask

Was ist Flask?
Flask ist ein leichtgewichtiges Python-Framework für Webanwendungen mit grundlegender Unterstützung für URL-Routing und Seitenrendering. Es gehört zur Kategorie der sogenannten Mikro-Frameworks, da es bewusst auf integrierte Funktionen wie Validierung, Datenbankabstraktion oder Authentifizierung verzichtet. Stattdessen können solche Features bei Bedarf über Flask-Erweiterungen eingebunden werden – spezielle Python-Pakete, die gezielt einzelne Funktionalitäten ergänzen.
Obwohl Flask keine eigene Template-Engine mitbringt, ist bei der Installation standardmäßig die Jinja-Vorlagenengine enthalten. Diese ermöglicht es, HTML-Seiten dynamisch zu generieren und mit Python-Daten zu verknüpfen – ein zentraler Bestandteil moderner Webentwicklung.
Warum Flask?
Flask ist besonders beliebt in der Ausbildung und bei Projekten, die schnell und flexibel umgesetzt werden sollen. Es eignet sich hervorragend für:
- den Einstieg in Webentwicklung mit Python,
- das Erstellen von Prototypen,
- die Integration von Machine-Learning-Modellen in Weboberflächen,
- REST-APIs und datengetriebene Anwendungen.
Was kann Flask?
Mit Flask kannst du:
- HTTP-Routen definieren (z. B.
/start,/kontakt,/api), - HTML-Seiten anzeigen (z. B. mit Jinja2-Templates),
- Formulare verarbeiten und Daten empfangen,
- JSON-Daten senden und empfangen (z. B. für KI-Modelle),
- Sessions und Cookies verwalten,
- externe Bibliotheken wie Bootstrap oder JavaScript einbinden.
Grundprinzip
Flask basiert auf einem einfachen Prinzip: Du definierst Funktionen in Python und verknüpfst sie mit bestimmten URLs. Wenn ein Benutzer eine URL aufruft, wird die zugehörige Funktion ausgeführt und liefert eine Antwort – z. B. eine HTML-Seite oder ein Ergebnis aus einem Modell.
Beispiel:
@app.route('/')
def home():
return 'Willkommen bei Flask!'
Technischer Hintergrund
- Flask wurde 2010 von Armin Ronacher entwickelt.
- Es basiert auf Werkzeug (für HTTP) und Jinja2 (für Templates).
- Es ist modular erweiterbar: Du kannst bei Bedarf Datenbanken, Authentifizierung oder Admin-Interfaces hinzufügen.
Begleitendes Video-Tutorial für diesen Block
Zur praktischen Vertiefung und als visuelle Anleitung werden wir in diesem Unterrichtsblock mehrfach auf das folgende Video zurückgreifen:
FLASK Python Tutorial für Anfänger – WebApps erstellen in 60 Minuten
Weiteres Flask Tutorial: https://code.visualstudio.com/docs/python/tutorial-flask
Installation von Flask in VS Code
Aufgabe:
Schaue dir das oben genannte Video bis Minute 14 an und fahre dann mit der Installation von Flask fort.
Wichtig!
Bevor wir mit Flask arbeiten können, müssen wir sicherstellen, dass unser Python-Interpreter korrekt eingerichtet ist und dass wir eine virtuelle Umgebung nutzen. Dies verhindert, dass Flask global installiert wird, und gibt uns die volle Kontrolle über die Bibliotheken, die in einem Projekt verwendet werden.
Schritt-für-Schritt-Anleitung
1. Python-Interpreter prüfen
- Öffne die Eingabeaufforderung (CMD) oder PowerShell.
- Gib ein:bash
python --version - Wenn der Befehl nicht funktioniert, muss der Speicherort des Python-Interpreters in der PATH-Umgebungsvariablen eingetragen werden.
- Beispiel:
C:\Users\<Name>\AppData\Local\Programs\Python\Python311\ - Danach erneut testen.
- Beispiel:
2. Projektordner erstellen
- Lege einen neuen Ordner für dein Projekt an, z. B.
flask_app. - Öffne diesen Ordner in Visual Studio Code.
3. Virtuelle Umgebung erstellen (wichtig!) Im VS Code-Terminal:
python -m venv venv
- Dadurch wird ein Unterordner
venverstellt, der alle Projektabhängigkeiten isoliert. - Vorteil: Flask wird nur für dieses Projekt installiert und nicht global.
4. Virtuelle Umgebung aktivieren
- Windows (PowerShell):bash
venv\Scripts\activate - Nach der Aktivierung erscheint
(venv)vor der Eingabezeile im Terminal.
5. Flask installieren Mit aktivierter virtueller Umgebung:
pip install flask
6. Installation überprüfen
python -m flask --version
Du solltest eine Ausgabe wie Flask 3.x.x sehen.
7. Erste Test-App erstellen Lege im Projektordner eine Datei app.py an:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'Hello Flask!'
if __name__ == '__main__':
app.run(debug=True)
8. App starten Im Terminal:
python app.py
9. Ergebnis prüfen Öffne im Browser:
http://127.0.0.1:5000/
Du solltest die Ausgabe „Hello Flask!“ sehen.
⚠️ Hinweis
- Die Verwendung einer virtuellen Umgebung ist entscheidend:
- Sie erleichtert die Wartung und vermeidet Versionskonflikte.
- Sie verhindert, dass Flask in der globalen Python-Umgebung installiert wird.
- Sie sorgt dafür, dass jede Anwendung ihre eigenen Bibliotheken hat.
Aufgabe: Dynamisches Routing testen
Ziel
Stelle sicher, dass Flask nicht nur statische Seiten, sondern auch dynamische Routen verarbeiten kann, indem du eine Variable direkt aus der URL ausliest.
Schritte zur Ausführung
1. Code anpassen und zwei Routen definieren
- Öffne die Datei
app.pyund ersetze den Code durch die folgende erweiterte Version. Diese Version definiert zwei Routen:- Die Startseite (
/)Eine dynamische Route (/gruss/<name>), die einen Teil der URL als Variable entgegennimmt.
from flask import Flask # Erstellt eine Instanz der Flask-Anwendung app = Flask(__name__) # 1. Statische Route (Startseite) @app.route('/') def index(): return 'Willkommen auf der Startseite. Versuche /gruss/DeinName' # 2. Dynamische Route mit Variable @app.route('/gruss/<name>') def personalisierter_gruss(name): # Die Variable 'name' wird automatisch aus der URL entnommen return f'Hallo, {name}! Deine Flask-Installation funktioniert mit dynamischen URLs.' if __name__ == '__main__': app.run(debug=True) - Die Startseite (
2. Anwendung neu starten
- Beende die laufende Flask-Anwendung im Terminal mit
CTRL + C. - Starte die Anwendung neu:Bash
python app.py
3. Testen der dynamischen Route
- Öffne den Webbrowser.
- Test 1 (Statische Route): Navigiere zur Startseite:
http://127.0.0.1:5000/ - Test 2 (Dynamische Route): Geben deinen Namen oder einen beliebigen Text in die URL ein:
http://127.0.0.1:5000/gruss/Max(Ersetze „Max“ durch deinen Namen)
Erfolgsnachweis
- Die Aufgabe ist erfolgreich abgeschlossen, wenn nach Aufruf von
/gruss/Maxim Browser der Text „Hallo, …! Deine Flask-Installation funktioniert mit dynamischen URLs.“ angezeigt wird.
Dies bestätigt, dass alle Kernkomponenten (virtuelle Umgebung, Flask, Routing) einsatzbereit sind.
JSON: Das Standard-Datenformat für Web-APIs
Flask wird in der modernen Webentwicklung primär dazu eingesetzt, Web-APIs (Schnittstellen) zu bauen. Diese APIs dienen dazu, strukturierte Daten mit anderen Programmen (wie JavaScript-Frontends, mobilen Apps oder anderen Servern) auszutauschen.
Die universelle Sprache für diesen Datenaustausch im Web ist JSON (JavaScript Object Notation).
Um von Anfang an funktionale Backend-Anwendungen erstellen zu können, führen wir daher zunächst den Abschnitt zur JSON-Analyse und -Erstellung ein. Dieses Wissen ist die Grundlage dafür, dass unsere Flask-Routen später nicht nur einfachen Text, sondern sinnvolle, strukturierte Daten verarbeiten und zurückgeben können.
1. Daten empfangen (Vom Client zum Flask-Server)
Wenn ein Webbrowser, eine mobile App oder ein anderes Programm Daten an Ihren Flask-Server sendet (z. B. wenn sich ein Benutzer registriert oder eine Bestellung aufgibt), werden diese Daten in der Regel als JSON-Payload gesendet.
- Flask bietet eine spezielle Methode, um diese eingehenden JSON-Daten direkt zu verarbeiten:
request.get_json()(oder das Attributrequest.json). - Diese Methode übernimmt die Arbeit des Python-internen
json-Moduls für Sie und konvertiert die JSON-Daten automatisch in ein Python-Dictionary (Wörterbuch), mit dem Sie dann in Ihrem Code einfach arbeiten können.- Ohne diese Konvertierung wären die Daten nur ein unverarbeiteter Text-String.
2. Daten senden (Vom Flask-Server zum Client)
Wenn Ihr Flask-Server eine Antwort an den Client schickt (z. B. eine Liste von Produkten), müssen Sie Ihre Python-Datenstrukturen (Dictionaries oder Listen) wieder in das JSON-Format umwandeln, damit der Client sie verstehen kann.
- In Flask können Sie oft einfach ein Python-Dictionary aus Ihrer View-Funktion zurückgeben, und Flask kümmert sich automatisch um die Serialisierung (Umwandlung) in JSON.
Zusammenfassend: JSON ist die Sprache der Web-APIs. Um mit Flask als API-Backend effektiv arbeiten zu können, müssen Sie wissen, wie diese JSON-Daten empfangen und in native Python-Datenstrukturen (wie z. B. Dictionaries und Listen) umgewandelt werden. Daher ist die JSON-Analyse ein unverzichtbarer Bestandteil des gesamten Themas „Flask und Webentwicklung“.
Aufgabe: API-Endpunkt für Produktinformationen
Ziel: Du erstellst einen einfachen API-Endpunkt (/produkt), der eine Anfrage im JSON-Format annimmt, die Daten verarbeitet (spezielles Feld herausliest) und eine formatierte JSON-Antwort zurückgibt.
Vorbereitung (Setup)
- Erstelle die Datei
api_app.pyin deinem Projektordner. - Importiere die notwendigen Flask-Module (
Flask,requestundjsonify).
Schritte zur Implementierung
1. API-Route definieren
- Definiere eine Route namens
/produkt. - Stelle sicher, dass diese Route sowohl die Methoden
GET(für das Senden von JSON) als auchPOST(für das Empfangen von JSON) erlaubt.
2. Daten empfangen (POST-Anfrage)
- Implementiere eine Logik in der Route, die prüft, ob die Anfrage per
POST-Methode gesendet wurde. - Lese die eingehende JSON-Nutzlast (
Payload) überrequest.get_json()in eine Variable (z. B.daten) ein. - Prüfe: Ob der Schlüssel
produktnamein den empfangenendatenvorhanden ist.
3. Daten verarbeiten und antworten
- Wenn der
produktnamevorhanden ist, erstelle ein Python-Dictionary als Antwort, das folgende Informationen enthält:status:"erfolg"eingegangen: Der Wert des empfangenenproduktname.verarbeitung:"Daten erfolgreich als Dictionary verarbeitet."
- Gib diese Antwort mithilfe der Funktion
jsonify()zurück. (jsonify()konvertiert dein Python-Dictionary automatisch in das korrekte JSON-Antwortformat).
4. Fehlerbehandlung (Optional)
- Wenn der
produktnamein der Anfrage fehlt, gib eine Fehlerantwort zurück:status:"fehler"meldung:"Produktname fehlt in der JSON-Payload."
Erwarteter Code-Aufbau (api_app.py)
from flask import Flask, request, jsonify
app = Flask(__name__)
# Erlaubt GET (Standard) und POST (für JSON-Daten)
@app.route('/produkt', methods=['GET', 'POST'])
def produkt_api():
# Dein Code hier
# ...
pass
if __name__ == '__main__':
app.run(debug=True)
Test der Anwendung
Da ein Browser keine POST-Anfrage mit JSON-Payload senden kann, musst du die Funktionalität mit einem Tool testen (z. B. Postman, VS Code-Erweiterungen wie REST Client, oder dem Python-Modul requests in einem neuen Skript).
- Starte die Anwendung:
python api_app.py - Sende eine POST-Anfrage an
http://127.0.0.1:5000/produktmit folgendem JSON-Body:
{
"produktname": "Webcam Pro X5",
"preis": 89.99
}
Erwartete JSON-Antwort:
{
"eingegangen": "Webcam Pro X5",
"status": "erfolg",
"verarbeitung": "Daten erfolgreich als Dictionary verarbeitet."
}
Strukturierung des Frontends mit HTML, CSS und Bootstrap
In modernen Webanwendungen spielt das Frontend eine zentrale Rolle: Es ist die Schnittstelle zwischen Nutzer und Anwendung und entscheidet maßgeblich darüber, wie intuitiv und angenehm die Interaktion erlebt wird. Während das Backend die Logik und Datenverarbeitung übernimmt, sorgt das Frontend dafür, dass diese Funktionen visuell zugänglich und verständlich dargestellt werden.
In diesem Abschnitt lernst du, wie du mit den grundlegenden Bausteinen der Webentwicklung – HTML zur Strukturierung von Inhalten und CSS zur Gestaltung des Layouts – eine klare und funktionale Oberfläche aufbaust. Um die Entwicklung effizienter und professioneller zu gestalten, nutzen wir zusätzlich die Bootstrap-Bibliothek, die eine Vielzahl vorgefertigter Komponenten und Design-Elemente bereitstellt. Bootstrap erleichtert es, responsive und ansprechende Oberflächen zu erstellen, die auf unterschiedlichen Geräten und Bildschirmgrößen gleichermaßen gut funktionieren.
Beispiel: Chat UI
Als praktisches Beispiel wirst du eine Chat-Oberfläche entwickeln. Dabei geht es nicht nur um die reine Darstellung von Nachrichten, sondern auch um die sinnvolle Anordnung von Eingabefeldern, Buttons und Ausgaben. Du lernst, wie man mit HTML die Grundstruktur definiert, mit CSS gezielt das Erscheinungsbild anpasst und mit Bootstrap elegante, wiederverwendbare Elemente integriert.
Aufgabe:
Schaue dir nun den Abschnitt: Chat UI mit HTML und CSS (ab ca. 13:58 bis 26:03) an und beantworte folgende Fragen:
Phase 1: Vorbereitung und Flask-Integration
- Templates-Ordner: Welchen speziellen Namen muss der Ordner haben, den du in deinem Flask-Projekt anlegst, damit die Anwendung die HTML-Dateien darin findet?
- Rückgabe-Funktion: Welche Funktion musst du in deiner Python-Route (
@app.route) verwenden, um eine HTML-Datei anzuzeigen, anstatt nur einen einfachen Text-String zurückzugeben?
Phase 2: Externe Bibliotheken und Grundstruktur
- Bootstrap-Zweck: Welchen primären Vorteil bietet dir das CSS-Framework Bootstrap bei der Erstellung der Chat-Oberfläche im Vergleich zur reinen Nutzung von eigenem CSS?
- Einbindung: An welcher Stelle in der
index.html-Datei bindest du die CSS-Links von Bootstrap ein (im<head>oder im<body>) und warum gerade dort?
Phase 3: Nachrichten-Layout und Ausrichtung
- Nachrichten-Trennwand: Welche Bootstrap-Klasse nutzt der Dozent, um die eigenen Chat-Nachrichten an das rechte Ende des Containers zu verschieben?
- CSS-Abstand: Mit welcher Bootstrap-Klasse kannst du einen vertikalen Abstand zwischen den einzelnen Nachrichten-Karten erzeugen (z. B. 3 Einheiten Abstand nach oben)?
Phase 4: Fixierte Eingabeleiste
- Datenversand: Welches HTML-Element verwendest du, um das Eingabefeld und den Senden-Button zu gruppieren, damit die darin enthaltenen Daten beim Klick auf den Button an den Flask-Server gesendet werden können?
- Fixierung: Welche kritische CSS-Eigenschaft (keine Bootstrap-Klasse, sondern reines CSS) nutzt der Dozent, um die Eingabeleiste fest am unteren Rand des Browserfensters zu verankern?
- Breiten-Berechnung: Da die Eingabeleiste fixiert wird, muss ihre Breite manuell angepasst werden. Welche CSS-Funktion ermöglicht es, die volle Breite (
100%) zu nutzen, aber gleichzeitig den Randabstand (160px) abzuziehen?
Vertiefung der Konzepte: Wie das Frontend funktioniert
1. Die Flask-Templating-Architektur
Bevor du HTML-Code schreiben kannst, muss Python wissen, wo diese Dateien liegen und wie es sie anzeigt.
- Der
templates-Ordner: In Flask muss du alle deine HTML-Dateien (Templates) in einem Unterordner mit dem exakten Namentemplatesablegen. Dies ist eine feste Konvention des Flask-Frameworks. Legst du die Dateien woanders ab, findet Flask sie nicht. - Die Funktion
render_template: Wenn eine deiner Routen (z. B.@app.route('/')) eine HTML-Seite ausgeben soll, verwendest du nichtreturn '<h1>...</h1>', sondern:
return render_template('index.html')
Diese Funktion weist Flask an, die angeforderte Datei aus dem speziellen Ordner zu laden und an den Browser zu senden.
2. Layout-Steuerung mit Flexbox (Bootstrap)
Um die Chat-Nachrichten korrekt auszurichten (eigene Nachrichten rechts, fremde Nachrichten links), nutzt du die Leistungsfähigkeit des CSS-Konzepts Flexbox, welches von Bootstrap vereinfacht wird.
d-flex(Display Flex): Du wendest diese Bootstrap-Klasse auf den Container an, der die Nachricht umgibt. Dadurch wird dieser Container zu einem Flex-Container, dessen Inhalt nun flexibel gesteuert werden kann.justify-content: Diese Eigenschaft steuert die horizontale Verteilung des Inhalts innerhalb des Flex-Containers.justify-content-end: Platziert die Nachricht am rechten Ende des Containers (perfekt für die eigene Nachricht).justify-content-start: Platziert die Nachricht am linken Ende des Containers (für alle fremden Nachrichten).
- Modularität: Durch die Nutzung von Bootstrap-Klassen wie
card(für den Nachrichten-Hintergrund) undmt-3(für den vertikalen Abstand) erstellst du eine saubere, mobile-freundliche Oberfläche, ohne Hunderte Zeilen eigenen CSS-Code schreiben zu müssen.
3. Das fixierte Formular: Positionierung mit reinem CSS
Die Eingabeleiste am unteren Bildschirmrand muss immer sichtbar bleiben, auch wenn du scrollst. Dies erfordert das Arbeiten mit klassischem CSS.
position: fixed: Dies ist die entscheidende CSS-Eigenschaft. Sie bewirkt, dass das Element aus dem normalen Dokumentenfluss entfernt wird und seine Position relativ zum Browserfenster beibehält. Egal, wie weit der Benutzer scrollt, das Element bleibt fixiert.- Die
calc()-Funktion: Da das fixierte Element keine automatischen Container-Ränder erbt, musst du seine Breite manuell anpassen, um es von Rand zu Rand mit korrektem Abstand darzustellen:
width: calc(100% - 160px);
Die Funktion calc() erlaubt es dir, die volle verfügbare Breite (100%) zu nehmen und davon den benötigten Gesamt-Randabstand (z. B. 80px links + 80px rechts = 160px) abzuziehen.
Aufgabe: Aufbau der Chat-UI
Ziel: Du erstellst die visuelle Struktur des Chat-Messengers (index.html), bindest das Styling-Framework Bootstrap ein und positionierst das Eingabefeld fixiert am unteren Bildschirmrand.
1. Flask-Templates vorbereiten
Du musst die HTML-Datei im korrekten Ordner ablegen, damit Flask sie anzeigen kann.
- Erstelle in deinem Hauptprojektordner den Unterordner
templates. - Erstelle in diesem Ordner die Datei
index.html. - Passe deine Python-Anwendung (
app.pyoderapi_app.py) so an, dass sie die neue Datei rendert. Importiere dazurender_templateund nutze es in deiner Haupt-Route (@app.route('/')).
2. HTML-Gerüst und Bootstrap einbinden
Du legst die Basisstruktur fest und bindest Bootstrap für das schnelle Design ein.
- Erstelle in
index.htmldas standardmäßige HTML5-Grundgerüst. - Binde die Bootstrap CSS-Links in den
<head>-Bereich deiner HTML-Datei ein. - Füge den Haupt-Container für den Inhalt (eine
divmit der Klassecontainer) in den<body>-Bereich ein.
3. Nachrichten-Darstellung simulieren
Du erstellst die visuelle Struktur für zwei Nachrichten – eine links und eine rechts ausgerichtet.
- Füge zwei separate Nachrichten-Blöcke (Karten oder ähnliche
div-Strukturen) in deinen Haupt-Container ein. - Richte die erste Nachricht links aus (simuliert eine fremde Nachricht).
- Richte die zweite Nachricht rechts aus (simuliert deine eigene Nachricht) mit der dafür notwendigen Flexbox-Klasse.
- Verwende die Bootstrap-Klassen (
mt-3,cardetc.), um beiden Nachrichten eine optische Struktur und Abstand zu geben.
4. Das fixierte Eingabe-Formular
Das Formular muss immer am unteren Rand haften bleiben, damit der Benutzer jederzeit eine Nachricht senden kann.
- Erstelle ein
<form>-Element und gib ihm die MethodePOST(auch wenn wir die Daten noch nicht verarbeiten). - Füge in dieses Formular das Text-Eingabefeld (
<input>) und den Senden-Button ein. - Erstelle im
<head>-Bereich einen<style>-Block für dein eigenes CSS. - Definiere in diesem
<style>-Block eine CSS-Klasse für das Formular, die:- Die Eigenschaft
position: fixedsetzt. - Die
bottom– undleft-Eigenschaften für die Positionierung setzt. - Die Breite unter Verwendung der
calc()-Funktion berechnet, um die Breite des Fensters abzudecken und gleichzeitig Platz für die Ränder zu lassen.
- Die Eigenschaft
Erfolgsnachweis
- Der Server läuft: Deine Flask-Anwendung (
app.pyoderapi_app.py) ist aktiv und die virtuelle Umgebung ist aktiviert. - Optisches Ergebnis: Wenn du die URL im Browser öffnest, siehst du eine Chat-Oberfläche mit:
- Einem Header.
- Mindestens zwei gestylten Nachrichten (eine links, eine rechts).
- Einem fixiert am unteren Rand positionierten Eingabefeld, das beim Scrollen an seiner Stelle bleibt.
Datenbank-Einrichtung und -Modellierung
Ein wesentliches Merkmal moderner Webanwendungen ist die Fähigkeit, Daten nicht nur kurzfristig zu verarbeiten, sondern sie auch dauerhaft zu speichern. Während einfache Flask-Apps zunächst nur auf flüchtige Informationen reagieren – etwa Eingaben, die direkt im Speicher verarbeitet und anschließend wieder verworfen werden – entsteht echter Mehrwert erst dann, wenn Inhalte wie Nachrichten, Benutzerinformationen oder Einstellungen auch nach einem Neustart der Anwendung verfügbar bleiben.
Dieser Block widmet sich genau dieser Frage: Wie kann deine Flask-Anwendung Nachrichten persistent speichern? Die Lösung liegt in der Anbindung einer Datenbank. Mithilfe einer Datenbank-Erweiterung für Flask, wie beispielsweise Flask-SQLAlchemy, lassen sich Daten strukturiert ablegen, verwalten und jederzeit wieder abrufen.
Warum Persistenz wichtig ist
- Nachhaltigkeit: Nachrichten oder andere Inhalte bleiben auch nach dem Schließen der Anwendung erhalten.
- Struktur: Daten werden in Tabellen organisiert und können effizient durchsucht und gefiltert werden.
- Skalierbarkeit: Mit einer Datenbank kann deine Anwendung wachsen und große Datenmengen verarbeiten.
- Praxisnähe: Persistenz ist ein Standard in professionellen Anwendungen – von Chat-Apps bis hin zu komplexen Plattformen.
1. Das Bedürfnis nach Persistenz
Bisher existiert der Chat nur so lange, wie die Flask-Anwendung läuft. Startest du den Server neu, wären alle Nachrichten verloren.
- Lösung: Um Daten dauerhaft zu speichern und abrufbar zu machen, benötigst du eine Datenbank.
- Datenbank-Typ: Im Video wird SQLite verwendet. Dies ist eine einfache, dateibasierte Datenbank (die gesamte Datenbank liegt in einer einzigen
.db-Datei) und ist ideal für kleine Projekte und den Einstieg.
2. Flask-SQLAlchemy (Der Datenbank-Konnektor)
Flask selbst hat keine integrierte Datenbankunterstützung. Du bindest diese Funktionalität über eine spezielle Flask-Erweiterung ein:
- Name:
Flask-SQLAlchemy. - Funktion: Diese Erweiterung ist ein sogenannter Object-Relational Mapper (ORM). Ein ORM dient als Übersetzer: Er erlaubt dir, mit Python-Objekten (Klassen) zu arbeiten, anstatt komplizierte SQL-Befehle schreiben zu müssen. Das macht die Datenbank-Interaktion für Python-Entwickler einfacher und sicherer.
- Installation: Die Installation erfolgt über den Paketmanager (
pip install flask-sqlalchemy).
3. Konfiguration der Datenbank-Verbindung
Bevor du die Datenbank nutzen kannst, musst du Flask mitteilen, wo die Datenbank-Datei liegt:
- App-Konfiguration: Dies geschieht über das
app.config-Dictionary in deiner Python-Anwendung. - Der Verbindungsparameter (
SQLALCHEMY_DATABASE_URI):Pythonapp.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.db'Dieser String definiert den Pfad zur Datenbank.sqlite:///teilt SQLAlchemy mit, dass es sich um eine SQLite-Datenbank handelt, unddb.dbgibt an, dass die Datei im Hauptordner des Projekts liegen soll. - Initialisierung: Anschließend wird die Erweiterung mit der App-Instanz verknüpft:
db = SQLAlchemy(app).
4. Datenmodellierung mit Klassen (ORM)
Der wichtigste Schritt ist die Definition des Datenmodells. Du nutzt das in früheren Blöcken erlernte Wissen über Klassen und die Objektorientierte Programmierung (OOP), um eine Tabelle in der Datenbank abzubilden.
- Klasse = Tabelle: Jede Python-Klasse, die von
db.Modelerbt, wird zu einer Tabelle in der Datenbank.- Beispiel: Eine Klasse
Message(db.Model)wird zur Datenbanktabellemessage.
- Beispiel: Eine Klasse
- Attribute = Spalten: Die Attribute (Eigenschaften), die du in der Klasse definierst, werden zu den Spalten der Tabelle.
Aufbau der Spalten (db.Column):
Jede Spalte benötigt einen Typ und bestimmte Parameter:
| Parameter | Beispiel | Erklärung |
| Primärschlüssel | db.Column(db.Integer, primary_key=True) | Eine eindeutige ID für jede Zeile. Muss für die Tabelle definiert werden und wird automatisch hochgezählt. |
| Datentyp | db.Column(db.String(200)) | Definiert, welche Art von Daten gespeichert werden (z. B. String für Text, Integer für ganze Zahlen, DateTime für Zeitstempel). |
| Optionalität | nullable=True | Gibt an, ob das Feld leer sein darf (True) oder unbedingt ausgefüllt werden muss (False). |
| Standardwert | default=datetime.datetime.utcnow | Legt fest, dass, wenn beim Speichern kein Wert übergeben wird, ein Standardwert verwendet wird (hier: das aktuelle Datum und die Uhrzeit). |
5. Erstellung der Datenbank-Datei (db.create_all())
Nachdem das Datenmodell (die Klasse) definiert wurde, erstellst du die Datenbank physisch:
- Befehl: Du führst den Befehl
db.create_all()einmalig aus. - Funktion: Dieser Befehl liest alle deine
db.Model-Klassen und erstellt die entsprechenden Tabellen in derdb.db-Datei.
Anschließend ist deine Anwendung bereit, um Daten mit diesem Modell zu speichern und abzurufen.
Aufgabe: Datenbank-Modellierung mit Flask-SQLAlchemy
Ziel: Du installierst die notwendige ORM-Erweiterung (Flask-SQLAlchemy), konfigurierst die Datenbankverbindung und definierst das Python-Klassenmodell für deine message-Tabelle.
Schaue dir den Abschnitt: Datenbank-Einrichtung und -Modellierung (von ca. 26:03 bis 35:15) an und setze die folgenden Schritte um.
1. Installation und Konfiguration
Du beginnst mit der Installation der Erweiterung und legst die Verbindung zur Datenbank-Datei fest.
- Erweiterung installieren:
- Stelle sicher, dass deine virtuelle Umgebung (
(venv)) aktiviert ist. - Installiere die notwendige Flask-Erweiterung, die als ORM dient:Bash
pip install flask-sqlalchemy
- Stelle sicher, dass deine virtuelle Umgebung (
- Imports vorbereiten:
- Öffne deine Haupt-Python-Datei (z. B.
app.py). - Importiere die benötigten Module für Datenbank und Datum:Python
from flask_sqlalchemy import SQLAlchemy from datetime import datetime
- Öffne deine Haupt-Python-Datei (z. B.
- Konfiguration der Verbindung:
- Füge nach der Initialisierung deiner Flask-App (
app = Flask(__name__)) die folgenden Konfigurationszeilen ein, um Flask mitzuteilen, dass du eine SQLite-Datenbank (db.db) nutzen möchtest:Python# Konfiguration der Datenbank-URI (SQLite-Datei im Hauptverzeichnis) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.db' # Deaktiviert unnötige Warnungen app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # Initialisierung der SQLAlchemy-Erweiterung db = SQLAlchemy(app)
- Füge nach der Initialisierung deiner Flask-App (
2. Datenmodell definieren (Die Message-Klasse)
Du definierst nun die Python-Klasse, die die Struktur deiner message-Tabelle in der Datenbank abbildet.
- Erstelle die Klasse:
- Definiere eine Klasse namens
Message, die vondb.Modelerbt:Pythonclass Message(db.Model): # ... Spaltendefinitionen folgen hier ... pass
- Definiere eine Klasse namens
- Spalten definieren:
- Füge die folgenden Spalten-Attribute in deine
Message-Klasse ein:id: Definiere dies alsdb.Integerund setze es alsprimary_key=True, damit jede Nachricht eine eindeutige, automatisch generierte Nummer erhält.user: Ein Textfeld (db.String) für den Nutzernamen (z. B. max. 200 Zeichen).content: Ein längeres Textfeld (db.String) für den eigentlichen Nachrichtentext.created_at: Ein Datumsfeld (db.DateTime) für den Zeitstempel. Setze hier dendefault-Wert aufdatetime.utcnow, damit das aktuelle Datum und die Uhrzeit beim Speichern automatisch hinzugefügt werden.
- Füge die folgenden Spalten-Attribute in deine
3. Datenbank physisch erstellen
Nachdem das Modell definiert wurde, musst du die Datenbank-Datei im Dateisystem erzeugen.
- Starte den Python-Interpreter:
- Beende deine Flask-App (
CTRL + C). - Starte den interaktiven Python-Interpreter im Terminal:Bash
python
- Beende deine Flask-App (
- Tabellen erstellen:
- Importiere das
db-Objekt und führe den Erstellungsbefehl aus:Pythonfrom app import app, db # Erstellt die Datenbank-Datei (db.db) und die Tabelle 'message' with app.app_context(): db.create_all() - Verlasse den Interpreter mit
exit().
- Importiere das
Erfolgsnachweis
- Du hast eine neue Datei
db.dbin deinem Projektordner. - Wenn du die Datenbank-Datei mit einem SQLite-Viewer öffnest, siehst du eine Tabelle namens
messagemit den vier definierten Spalten (id,user,content,created_at).
Bist du mit diesen Schritten fertig, ist dein Backend bereit, um Nachrichten dauerhaft zu speichern und abzurufen!
Datenfluss und Speicherung von Nachrichten in Datenbanken
Eine Webanwendung entfaltet ihren vollen Nutzen erst dann, wenn sie nicht nur Eingaben entgegennimmt, sondern diese auch strukturiert verarbeitet und dauerhaft speichert. In diesem Abschnitt lernst du den gesamten Ablauf kennen, den eine Nachricht in deiner Flask-Anwendung durchläuft – von der Eingabe im HTML-Formular über die Verarbeitung im Routing bis hin zur Speicherung im Datenbankmodell (db.db).
Warum ist dieser Weg wichtig?
- Ganzheitliches Verständnis: Du siehst, wie Frontend und Backend zusammenarbeiten.
- Praxisnähe: Jede moderne Anwendung – ob Chat, Blog oder Forum – benötigt diesen Ablauf.
- Verknüpfung von Wissen:
1. Dynamische Routen zur Benutzeridentifikation
Zunächst musst du den Benutzer identifizieren, der die Nachricht sendet. Im Video wird dies einfach über die URL gelöst.
- Syntax: Die Route wird angepasst, um einen dynamischen Teil zu akzeptieren (z. B.
@app.route('/<name>')). - Funktionale Übergabe: Der in der URL eingegebene Wert (z. B. „Yunus“) wird automatisch als Parameter an die zugehörige Python-Funktion übergeben:
@app.route('/<name>')
def index(name): # Der 'name'-Parameter fängt den URL-Wert auf
# ...
- Zweck: Dieser Wert wird später verwendet, um die Nachricht in der Datenbank eindeutig dem entsprechenden Benutzer zuzuordnen.
2. HTTP-Methoden: GET und POST
Wenn du das Formular in deinem Frontend absendest, nutzt der Browser die POST-Methode, da du neue Daten an den Server schickst. Deine Flask-Route muss diese Methode explizit erlauben.
- Die
GET-Methode (Standard): Wird verwendet, um Daten abzurufen (z. B. wenn du eine Seite im Browser öffnest oder eine API abfragst). - Die
POST-Methode (Daten senden): Wird verwendet, um neue Daten an den Server zu senden (z. B. beim Absenden eines Formulars, Registrierung oder Speichern einer Nachricht). - Aktivierung: Du erweiterst den Route-Decorator um das Argument
methods:Python@app.route('/<name>', methods=['GET', 'POST']) - Logik-Trennung: Innerhalb der View-Funktion prüfst du mit der Anweisung
if request.method == 'POST':, ob eine Formular-Übermittlung stattgefunden hat. Die gesamte Logik zum Speichern der Daten wird nur in diesemif-Block ausgeführt.
3. Empfangen von Formular-Daten (request.form)
Die Nachricht (der eigentliche Text) wird vom HTML-Formular gesendet und muss vom Backend entgegengenommen werden.
- Das
request-Objekt: Dieses Objekt von Flask enthält alle Informationen zur eingehenden HTTP-Anfrage. request.form: Da die Daten von einem Standard-HTML-Formular gesendet werden (nicht als reines JSON), musst du das Dictionaryrequest.formverwenden. Dieses speichert die übermittelten Schlüssel-Wert-Paare aus dem Formular.- Schlüssel-Erkennung: Um auf den eingegebenen Nachrichtentext zuzugreifen, verwendest du den
name-Wert des entsprechenden HTML-Input-Feldes (z. B.request.form['content']).
4. Daten speichern mit dem ORM
Nachdem du den Benutzernamen (aus der URL) und den Nachrichtentext (aus dem Formular) hast, verwendest du dein zuvor erstelltes Datenmodell (Message-Klasse), um die Daten zu speichern.
- Instanziierung: Du erstellst ein neues Python-Objekt und übergibst die gesammelten Werte:
neue_nachricht = Message(user=name, content=inhalt)
2. Hinzufügen zur Session: Du teilst dem ORM mit, dass dieses neue Objekt zur Datenbank hinzugefügt werden soll:
db.session.add(neue_nachricht)
3. Festschreiben (Commit): Dies ist der entscheidende Schritt. Die commit()-Funktion schreibt die Änderungen dauerhaft in die db.db-Datei:
db.session.commit()
Nach diesem commit ist die Nachricht gespeichert und kann von nun an dauerhaft aus der Datenbank abgerufen werden.
Aufgabe: Speichern der Chat-Nachricht
Ziel: Du erweiterst deine Haupt-Route, um die POST-Methode zu verarbeiten, den Nutzernamen aus der URL und den Nachrichtentext aus dem Formular auszulesen und diese Daten dauerhaft in deiner db.db-Datenbank zu speichern.
Schaue dir den Abschnitt: Datenfluss und Speichern (von ca. 35:29 bis 44:40) an.
1. Route und Methode anpassen
Deine Route muss nun in der Lage sein, den Nutzernamen aus der URL zu erfassen und Formular-Daten über die POST-Methode entgegenzunehmen.
- Route erweitern: Passe den
@app.route-Decorator deiner Haupt-Route in deiner Python-Datei (app.pyoderapi_app.py) an:- Füge den Platzhalter
<name>hinzu, um den Benutzernamen aus der URL zu erfassen (z. B./max). - Erlaube explizit die Methode
POST.
- Füge den Platzhalter
- Imports erweitern: Importiere das
request-Objekt, das du für die Abfrage der Methode und der Formulardaten benötigst:
from flask import Flask, render_template, request # 'request' hinzufügen
# ... weitere Imports ...
- Funktions-Signatur: Passe die Funktion, die zur Route gehört, an, damit sie den
name-Parameter entgegennimmt:
@app.route('/<name>', methods=['GET', 'POST'])
def index(name):
# ...
2. Formular-Daten empfangen und speichern
Du implementierst die Logik, die nur bei einer POST-Anfrage ausgeführt wird.
- POST-Logik einleiten: Starte eine
if-Bedingung, um zu prüfen, ob die Anfrage eine Formular-Übermittlung war:Pythonif request.method == 'POST': # Hier kommt die Speicherlogik hin pass
if request.method == 'POST':
# Hier kommt die Speicherlogik hin
pass
- Nachricht auslesen: Innerhalb des
if-Blocks liest du den eingegebenen Nachrichtentext aus dem Formular aus. Tipp: Denke daran, den korrektenname-Wert des<input>-Feldes aus deinerindex.htmlzu verwenden, um auf dasrequest.form-Dictionary zuzugreifen. - ORM-Objekt erstellen: Erstelle eine neue Instanz deiner
Message-Klasse, indem du denname(aus der URL) und den ausgelesenen Nachrichtentext alscontentübergibst. - Datenbank-Operationen: Verwende die
db.session-Methoden, um die neue Nachricht zur Datenbank hinzuzufügen und die Änderung festzuschreiben:- Füge das erstellte Objekt hinzu (
.add()). - Bestätige die Transaktion (
.commit()).
- Füge das erstellte Objekt hinzu (
3. HTML-Formular anpassen
Damit das Formular die Daten korrekt sendet, musst du dein <form>-Tag in der index.html anpassen.
- Formular-Methode: Stelle sicher, dass das
<form>-Tag die MethodePOSTverwendet. - Input-Name: Vergewissere dich, dass das
<input>-Feld für den Nachrichtentext einname-Attribut besitzt, damit Flask es überrequest.formerkennen kann (z. B.<input name="text_input">).
Erfolgsnachweis
- Starte deine Flask-Anwendung neu (z. B. unter
http://127.0.0.1:5000/DeinName). - Gib im Eingabefeld einen Text ein und klicke auf Senden.
- Prüfe mit einem SQLite-Viewer (oder durch die im Video gezeigte Methode), ob eine neue Zeile in der Tabelle
messagedeinerdb.db-Datei mit deinem Namen und dem eingegebenen Text hinzugefügt wurde.
Ist die Zeile gespeichert, funktioniert der komplette Datenfluss vom Browser bis zur Datenbank!
Auslesen und Dynamisches Anzeigen
Nachdem du gelernt hast, wie Nachrichten in deiner Flask-Anwendung dauerhaft in der Datenbank gespeichert werden können, folgt nun der nächste entscheidende Schritt: die gespeicherten Daten wieder abrufen und im Browser darstellen. Denn eine Anwendung gewinnt erst dann an praktischer Bedeutung, wenn Nutzer ihre eingegebenen Inhalte auch visuell zurückerhalten und mit ihnen interagieren können.
Hier kommt die Jinja2-Template-Engine ins Spiel, die tief in Flask integriert ist. Jinja2 ermöglicht es dir, HTML-Seiten dynamisch zu gestalten, indem du Daten aus dem Backend direkt in die Oberfläche einbindest. So kannst du beispielsweise eine Liste aller gespeicherten Nachrichten aus der Datenbank abrufen und diese in einem übersichtlichen Layout im Browser anzeigen.
Warum Jinja2 wichtig ist
- Dynamische Inhalte: Daten aus der Datenbank werden automatisch in HTML-Seiten eingefügt.
- Trennung von Logik und Darstellung: Python-Code bleibt im Backend, während das Frontend über Templates gepflegt wird.
- Flexibilität: Mit Schleifen, Bedingungen und Platzhaltern lassen sich komplexe Layouts einfach umsetzen.
- Praxisnähe:
1. Daten aus der Datenbank abrufen
Bevor du die Daten an das HTML senden kannst, musst du sie mithilfe des ORM (Flask-SQLAlchemy) aus der db.db-Datei auslesen.
- Der Abruf: Du verwendest die
query-Methode deinerMessage-Klasse, um alle gespeicherten Nachrichten abzufragen.Message.query: Startet die Abfrage für diemessage-Tabelle..order_by(Message.created_at): Sortiert die Ergebnisse nach dem Zeitstempel, um sicherzustellen, dass die ältesten Nachrichten zuerst angezeigt werden..all(): Führt die Abfrage aus und gibt eine Liste vonMessage-Objekten zurück (z. B.[<Message 1>, <Message 2>, ...]).
nachrichten = Message.query.order_by(Message.created_at).all()
- Das Ergebnis: Deine Python-Variable
nachrichtenenthält nun alle Daten, die du im Chat anzeigen möchtest.
2. Datenübergabe von Python an HTML
HTML kann standardmäßig keine Python-Variablen oder Listen verarbeiten. Flask löst dieses Problem durch die Template-Engine Jinja2:
- Der Parameter: Die Funktion
render_template()kann zusätzliche Schlüsselwortargumente entgegennehmen, die dann automatisch als Variablen im HTML-Template verfügbar sind. - Im Template: Im
index.html-Template ist die Variableall_messagesnun direkt verfügbar und enthält die Liste der Nachricht-Objekte.
# Übergabe der Python-Liste 'nachrichten' an das Template unter dem Namen 'all_messages'
return render_template('index.html', user_name=name, all_messages=nachrichten)
3. Dynamische Darstellung mit Jinja2
Innerhalb deiner index.html-Datei verwendest du spezielle Jinja2-Syntax, um die übergebenen Daten zu verarbeiten und anzuzeigen.
A. Variable Substitution ({{ ... }})
Dieser Syntax dient dazu, den Wert einer Variable direkt in den HTML-Code einzufügen.
- Beispiel: Um den Namen des angemeldeten Benutzers in der Überschrift anzuzeigen:
<h1>Willkommen, {{ user_name }}</h1>
B. Kontrollstrukturen ({% ... %})
Dieser Syntax dient dazu, logische Anweisungen und Kontrollstrukturen wie Schleifen und Bedingungen auszuführen.
- Die
for-Schleife: Du nutzt einefor-Schleife, um die gesamte Liste der Nachrichten zu durchlaufen und für jede Nachricht den HTML-Code (diediv-Struktur für die Karte) einmal zu generieren.Innerhalb der Schleife greifst du auf die Attribute des aktuellenmsg-Objekts zu, z. B.{{ msg.content }}oder{{ msg.user }}.
{% for msg in all_messages %}
<!-- Hier wird die Nachricht-Karte eingefügt -->
{% endfor %}
4. Styling-Logik im Template (Dynamische Ausrichtung)
Die letzte Herausforderung besteht darin, die richtige CSS-Klasse (justify-content-end vs. justify-content-start) dynamisch zuzuweisen, damit die Nachrichten des aktuellen Benutzers rechts und alle anderen links angezeigt werden.
- Die
if-Bedingung: Du verwendest eine Jinja2-Bedingung ({% if ... %}) innerhalb der Schleife. - Der Vergleich: Du vergleichst den
user-Wert der aktuellen Nachricht (msg.user) mit dem Namen des Benutzers, der gerade angemeldet ist (user_name, der aus der URL übergeben wurde).
{% if msg.user == user_name %}
<!-- Eigene Nachricht: rechts ausrichten -->
<div class="d-flex justify-content-end">...</div>
{% else %}
<!-- Fremde Nachricht: links ausrichten -->
<div class="d-flex justify-content-start">...</div>
{% endif %}
Durch diese Logik wird deine statische HTML-Struktur in eine voll funktionsfähige, dynamische Chat-Oberfläche verwandelt.
Aufgabe:
Ziel: Du implementierst die Logik zum Auslesen aller gespeicherten Nachrichten aus der Datenbank und nutzt die Jinja2-Template-Engine, um diese Daten in deiner index.html-Datei als Chat-Karten darzustellen.
Schaue dir den abschließenden Abschnitt: Auslesen und Dynamisches Anzeigen (von ca. 45:00 bis zum Ende) an.
1. Datenabfrage in Python
Du beginnst damit, alle gespeicherten Nachrichten in deiner Haupt-Route abzurufen.
- Abfrage implementieren: Finde in deiner Python-Anwendung (
app.pyoderapi_app.py) die Hauptfunktionindex(name). - Nachrichten abrufen: Schreibe eine Abfrage mit dem ORM, die alle Nachrichten aus der
message-Tabelle abruft und sie nach dem Erstellungsdatum (created_at) sortiert:
# Speichere das Ergebnis in einer Variablen namens 'all_messages'
all_messages = Message.query.order_by(Message.created_at).all()
- Daten übergeben: Erweitere den
return render_template(...)-Aufruf, um die abgerufene Liste (all_messages) und den Benutzernamen (name) an dein HTML-Template zu übergeben:
return render_template('index.html', current_user=name, messages=all_messages)
(Hinweis: Wir nutzen hier die Variablennamen current_user und messages zur besseren Unterscheidung im Template.)
2. Dynamische Darstellung in HTML (Jinja2)
Du ersetzt nun die statischen, simulierten Nachrichten in deiner index.html durch dynamische Jinja2-Logik.
- Statischen Code entfernen: Lösche die beiden
<div class="card">-Blöcke, die du in der vorherigen Aufgabe zur Simulation eingefügt hast. Lasse nur den Haupt-Container (<div class="container">) übrig. for-Schleife implementieren: Füge die Jinja2-Syntax ein, um die übergebene Listemessageszu durchlaufen:
{% for msg in messages %}
<!-- Hier kommt die Nachricht-Karte für {{ msg.content }} hinein -->
{% endfor %}
- Nachrichteninhalt einfügen: Kopiere den HTML-Code (inklusive der Bootstrap-Klassen wie
cardundcard-body) wieder in die Schleife. Ersetze den statischen Text durch die Variablen-Substitution:- Zeige den Namen des Autors mit
{{ msg.user }}an. - Zeige den Inhalt mit
{{ msg.content }}an. - Füge den Zeitstempel mit
{{ msg.created_at }}(ggf. mit dem Jinja2-Filter für eine schönere Formatierung) hinzu.
- Zeige den Namen des Autors mit
3. Dynamische Ausrichtung (Eigene vs. Fremde Nachricht)
Du nutzt eine if-Bedingung innerhalb der Schleife, um die Nachrichten von dir selbst immer rechts auszurichten.
- Bedingte Klasse: Verwende eine Jinja2-Bedingung, die den Autor der aktuellen Nachricht (
msg.user) mit dem angemeldeten Benutzer (current_user) vergleicht. - Implementiere die Logik: Lasse Jinja2 entscheiden, welche CSS-Klasse für die Ausrichtung des äußeren
divverwendet werden soll:
{% if msg.user == current_user %}
<div class="d-flex justify-content-end mt-3">
{% else %}
<div class="d-flex justify-content-start mt-3">
{% endif %}
(Hinweis: Schließe die if-Bedingung am Ende des Nachrichtenblocks mit {% endif %} ab, und vergiss nicht, die for-Schleife mit {% endfor %} zu schließen.)
Erfolgsnachweis
- Starte deine Flask-Anwendung neu (z. B. unter
http://127.0.0.1:5000/DeinName). - Prüfe im Browser: Alle Nachrichten, die du in der vorherigen Aufgabe gespeichert hast, sollten nun angezeigt werden.
- Test der Dynamik: Wechsle den Namen in der URL (z. B.
/FremderName). Alle Nachrichten, die vorher links angezeigt wurden, bleiben links. Die Nachrichten, die du alsFremderNameneu eingibst, sollten nun auf der rechten Seite erscheinen.
Mit dieser Aufgabe hast du den kompletten Zyklus einer Webanwendung – von der Datenbank über das Backend bis zum Frontend – abgeschlossen.
Weitere Übungen Objektorientierte Programmierung: https://informatik.bildung-rp.de/fileadmin/user_upload/informatik.bildung-rp.de/Fortbildung/pdf/PY-110831-Savoric-OOP.pdf
