Wir haben in den drei vorherigen Artikeln ein erstes Beispiel für die Programmierung einer Web-API unter Flask gesehen, wie man eine Web-API mit einer SqLite-Datenbank verbindet und wie man eine Web-API mit Python, Flask, Swagger und Connexion programmiert und dokumentiert. In diesem und dem nächsten Artikel werden wir uns ansehen, wie man diese Elemente mit der Verwaltung einer Datenbank unter Verwendung von SQLAlchemy und Marshmallow kombiniert.
1. Technische Voraussetzungen
Beginnen wir mit der Installation der Python-Bibliotheken, die wir unter Anaconda benötigen, mit dem folgenden Befehl in einem Terminalfenster:
- $ conda install -c conda-forge Flask-SQLAlchemy flask- marshmallow marshmallow-sqlalchemy marshmallow.
Mit diesem Befehl werden die folgenden Bibliotheken installiert:
- flask-SQLAlchemy, die den Zugriff auf Datenbanken in einer mit Flask kompatiblen Weise ermöglicht.
- flask-marshmallow, mit der du Python-Objekte in serialisierbare Strukturen umwandeln kannst (d. h. die als eine Folge von kleineren Informationen kodiert werden können).
- marshmallow-sqlalchemy, das die Serialisierung und Deserialisierung von Python-Objekten ermöglicht, die von SQLAlchemy erzeugt wurden.
- marshmallow, das den Großteil der Marshmallow-Funktionen zur Serialisierung/Deserialisierung bereitstellt.
2. Allgemeiner Überblick
Im dritten Artikel wurde gezeigt, wie man mithilfe von Python, Flask, Connexion und Swagger eine REST-API erstellt, die CRUD-Operationen zulässt und gut dokumentiert ist.
Die Daten wurden jedoch in einer PEOPLE-Struktur gespeichert, die bei jedem Start des Servers zurückgesetzt wurde.
Im Folgenden werden wir uns ansehen, wie man die PEOPLE-Struktur und die von der API bereitgestellten Aktionen mithilfe von SQLAlchemy und Marshmallow dauerhaft in einer Datenbank speichert.
SQLAlchemy bietet ein Object Relational Model (ORM), das es ermöglicht, die Daten von Python-Objekten in einer datenbankähnlichen Darstellung zu speichern.
Dies ermöglicht es, weiterhin im Sinne von Python zu programmieren, ohne sich um die Details der Darstellung der Daten eines Objekts in der Datenbank kümmern zu müssen.
Marshmallow bietet Werkzeuge, um Python-Objekte zu serialisieren und deserialisieren, während sie von unserer REST-API im JSON-Format gesendet oder empfangen werden.
Marshmallow wandelt insbesondere Instanzen von Python-Klassen in Objekte um, die in JSON umgewandelt werden können.
3. Daten
Im dritten Artikel hatten wir unsere Daten durch ein Python-Wörterbuch dargestellt, dessen Schlüssel die Nachnamen der in der Datenbank vorhandenen Personen waren.
Der entsprechende Code lautete wie folgt:
Die Änderungen, die du vornehmen wirst, werden die Daten in eine Tabelle einer Datenbank übertragen.
Das bedeutet, dass die Daten „fest“ gespeichert werden und zwischen zwei Ausführungen von server.py zugänglich sind.
Da der Nachname der Schlüssel zum Python-Wörterbuch PEOPLE war, verhinderte der Code, dass der Name einer Person geändert werden konnte. Die Übertragung in eine Datenbank wird diese Änderung erlauben, da der Nachname nicht mehr als Schlüssel verwendet wird.Konzeptionell kann man sich eine Datenbank als eine zweidimensionale Tabelle vorstellen, in der jede Zeile einem Datensatz und jede Spalte einem Feld des Datensatzes entspricht.
Datenbanken verfügen oft über einen selbst erstellten Index, mit dem die Zeilen identifiziert werden können.
Dies wird als Primärschlüssel bezeichnet.
Jedem Datensatz in der Tabelle entspricht ein eindeutiger Wert des Primärschlüssels.
Die Tatsache, dass wir einen von unseren Daten unabhängigen Primärschlüssel haben, erlaubt es uns, jedes andere Feld in den Datensätzen zu ändern, einschließlich des Nachnamens.
Wir übernehmen eine Standardkonvention für Datenbanken, indem wir der Tabelle einen singulären Namen geben: Wir nennen sie person.
4. Interaktion mit der Datenbank
Wir werden SQLite als Datenbank-Engine verwenden, um die PEOPLE-Daten zu speichern.
SQLite ist die meistverbreitete Datenbank der Welt und steht standardmäßig unter Python zur Verfügung.
Sie ist schnell, arbeitet mit Dateien und eignet sich für viele Arten von Projekten.
Es handelt sich um ein vollständiges RDBMS (Relational DataBase Management System), das auch SQL beinhaltet.
Angenommen, die Tabelle person existiert in einer SQLite-Datenbank.
Dann kann man SQL verwenden, um die Daten abzurufen.
Im Gegensatz zu Programmiersprachen wie Python sagt SQL nicht, wie die Daten abgerufen werden sollen, sondern gibt nur an, welche Daten benötigt werden, und überlässt das Wie der Datenbankmaschine.
Eine SQL-Abfrage, um eine Liste aller Personen in unserer Datenbank abzurufen, die alphabetisch nach Nachnamen sortiert ist, sieht folgendermaßen aus:
SELECT * FROM person ORDER BY ‚lname‘;
Du kannst die obige Abfrage nach dem Start von SQLite mit folgendem Befehl in einem Terminalfenster absetzen
$ sqlite3 people.db
was zu folgendem Ergebnis führt:
SHELL
sqlite> select * from person order by ‚lname‘;
1|Dupont|Jean|2022-04-24 09:30:48.713385
2|Durand|Louise|2022-04-24 09:30:48.715009
3|Lopez|Francois|2022-04-24 09:30:48.716247
Die vorherige Ausgabe entspricht allen Datensätzen der Datenbank, wobei die einzelnen Felder durch das „Pipe“-Zeichen | getrennt sind.
Tatsächlich ist es möglich, die vorherige Abfrage direkt von Python aus zu übermitteln. Das Ergebnis wäre eine Liste von Tupeln. Die Liste enthält alle Datensätze, wobei jedes Tupel einem Datensatz entspricht.
Die Daten auf diese Weise abzurufen, ist jedoch nicht wirklich mit dem Geist von Python vereinbar.
Eine Liste zu erhalten passt gut zu Python, aber Tupel zu haben ist problematisch: Der Programmierer muss den Index jedes Feldes kennen, um auf die Daten zugreifen zu können.
Das vorherige Programm läuft folgendermaßen ab:
Zeile 1 importiert das Modul sqlite3.
Zeile 3 erstellt eine Verbindung zur Datenbankdatei.
Zeile 4 erstellt einen Verbindungscursor (der die Datenbank durchsuchen wird).
Zeile 5 verwendet den Cursor, um eine SQL-Anfrage auszuführen, die als Zeichenkette ausgedrückt wird.
Zeile 6 ruft alle von der SQL-Abfrage zurückgegebenen Datensätze ab und weist sie der Variablen people zu.
Die Zeilen 7 und 8 iterieren über die Liste people und drucken den Vor- und Nachnamen jeder Person aus.
Die Variable people hat folgende Struktur:
[(1, ‚Dupont‘, ‚Jean‘, ‚2022-04-24 09:30:48.713385‘),
(2, ‚Durand‘, ‚Louise‘, ‚2022-04-24 09:30:48.715009‘),
(3, ‚Lopez‘, ‚Francois‘, ‚2022-04-24 09:30:48.716247‘)]
Das Ergebnis der Ausführung des vorherigen Programms ist wie folgt:
- Jean Dupont
- Louise Durand
- Francois Lopez
Im vorherigen Programm muss man wissen, dass der Nachname an Position 1 und der Vorname an Position 2 in den zurückgegebenen Tupeln steht.
Außerdem muss die interne Struktur von person bekannt sein, wenn man die Iterationsvariable person als Parameter an eine Funktion oder Methode übergibt.
Es wäre viel praktischer, person als Python-Objekt zu haben, wobei die verschiedenen Felder den Attributen des Objekts entsprechen.
Dies kann mit SQLAlchemy gemacht werden.
>> Auch interessant: OLAP Datenbankstruktur
5. Das Risiko der SQL-Injection
Im vorherigen Programm ist die SQL-Abfrage eine einfache Zeichenkette, die zur Ausführung an die Datenbank übergeben wird.
Dies ist kein Problem, da die Zeichenkette vollständig unter der Kontrolle des Programmierers steht.
Im Gegensatz dazu wird unsere Webanwendung von einer Benutzereingabe in die REST-API ausgehen, um eine SQL-Abfrage zu erstellen.
Dies kann eine Sicherheitslücke in unserer Anwendung darstellen.
Die REST-API-Anfrage für den Zugriff auf eine bestimmte Person hat die Form :
GET /api/people/{lname}.
Das bedeutet, dass die API eine Variable, lname, im URL-Pfad erwartet, die sie verwendet, um eine bestimmte Person zu finden.
Der entsprechende Python-Code hat die folgende Form:
Der vorherige Code funktioniert wie folgt:
In Zeile 1 wird der Wert der Variablen lname auf ‚Müller‘ gesetzt. Dies würde aus dem URL-Pfad im Rahmen der API stammen.
Der von Zeile 2 erzeugte SQL-Code hat folgende Form:
SQL
SELECT * FROM person WHERE lname = ‚Dupont‘.
Wenn diese SQL-Abfrage von der Datenbank ausgeführt wird, wird die entsprechende Person in der Datenbank gesucht und die zugehörigen Informationen abgerufen.
Dies ist die erwartete Funktionsweise; ein böswilliger Benutzer kann jedoch in diesem Arbeitsrahmen eine SQL-Injektion (SQL-Injection) durchführen.
Zum Beispiel könnte ein böswilliger Benutzer die API wie folgt anfordern:
GET /api/people/Farrell‘);DROP TABLE person;
Die vorherige Abfrage setzt den Wert der Variablen lname auf: ‚Müller‘);DROP TABLE person;‘, wodurch die folgende SQL-Abfrage erzeugt wird:
SQL
SELECT * FROM person WHERE lname = ‚Dupont‘);DROP TABLE person;
Die vorherige SQL-Abfrage ist gültig.
Sie würde die Tabelle zunächst nach einer Person namens „Müller“ durchsuchen und dann, wenn sie auf ein Semikolon stößt, zur nächsten Abfrage übergehen, die die gesamte Tabelle person löscht.
Es ist klar, dass dies unsere Anwendung außer Gefecht setzen würde.
Eine Möglichkeit, dies zu vermeiden, besteht darin, die von den Nutzern gelieferten Daten zu filtern, um sicherzustellen, dass sie nichts enthalten, was für die Anwendung gefährlich ist.
Dies kann kompliziert sein, vor allem, da man alle Interaktionen der Nutzer mit der Datenbank untersuchen müsste.
Eine praktischere Lösung ist die Verwendung von SQLAlchemy.
SQLAlchemy filtert die Anfragen der Benutzer an die Datenbank, bevor es die entsprechenden SQL-Abfragen erstellt.
Dies ist ein weiterer Grund, SQLAlchemy zu verwenden, wenn du mit webbasierten Datenbanken arbeitest.
6. Das SQLAlchemy-Modell
QLAlchemy ist ein wichtiges Projekt und stellt sehr viele Werkzeuge zur Verfügung, mit denen man unter Python mit Datenbanken arbeiten kann.
Eines der Werkzeuge, die es zur Verfügung stellt, ist ein ORM (Object Relational Mapper).
Diesen werden wir benutzen, um die Datenbank person in Python zu erstellen und mit ihr zu arbeiten.
Die objektorientierte Programmierung (OOP) ermöglicht es, Daten und ihre Manipulation, d. h. die Funktionen, die auf den Daten operieren, miteinander zu verknüpfen.
Durch das Erstellen von SQLAlchemy-Klassen können Datenbankfelder mit bestimmten Arten von Manipulationen verknüpft werden, wodurch eine Interaktion mit den Daten ermöglicht wird.
Die SQLAlchemy-Klassen definieren die Daten in der Tabelle person wie folgt:
Die Klasse Person erbt von db.model, das die Attribute und Funktionen für die Manipulation von Datenbanken bereitstellt.
Die restlichen Definitionen entsprechen den Attributen der Klasse.
__tablename__ = person stellt die Verbindung zwischen der Klasse Person und der Tabelle person her.
person_id = db.Column(db.Integer, primary_key = True) erstellt eine Spalte in der Datenbank, die eine ganze Zahl enthält, die als Primärschlüssel verwendet wird. Dies impliziert, dass person_id ein selbstinkrementierter Integerwert ist.
lname = db.Column(db.String) erstellt das Feld „Nachname“, d. h. eine Spalte in der Datenbank, die Zeichenketten enthält.
name = db.Column(db.String) erstellt das Feld „vorname“, d. h. eine Spalte in der Datenbank, die Zeichenketten enthält.
timestamp = db.Column(db.DateTime, default = datetime.utcnow, on update = datetime.utcnow)erstellt das Feld „timestamp“, d. h. eine Spalte in der Datenbank, die Datums-/Zeitwerte enthält.
Der Parameter default = datetime.utcnow setzt als Standardwert für den „Zeitstempel“ den aktuellen Wert utcnow, wenn ein Datensatz erstellt wird. Ebenso aktualisiert update = datetime.utcnow den „timestamp“ mit dem Wert utcnow, wenn der Datensatz aktualisiert wird.
Warum wird utcnow als „timestamp“ verwendet?
Tatsächlich gibt die Methode datetime.utcnow( )den aktuellen UTC-Zeitpunkt zurück (UTC: Universal Time Coordinated). Dies ist eine Möglichkeit, den „Zeitstempel“ zu standardisieren.
Dies ermöglicht insbesondere einfachere Berechnungen von Datums-/Zeitangaben.
Wenn man von verschiedenen Zeitzonen aus auf die App zugreift, reicht es, die Zeitzone zu kennen, um die Daten/Zeiten umzurechnen.
Es wäre komplizierter, wenn man einen „Zeitstempel“ in lokale Daten/Zeiten verwenden würde.
Was bringt uns die Klasse Person?
Die Idee ist, die Tabelle mithilfe von SQLAlchemy abzufragen und als Ergebnis eine Liste von Instanzen der Klasse Person zu erhalten.
Als Beispiel nehmen wir die vorherige SQL-Abfrage:
SQL
SELECT * FROM people ORDER BY lname;
Mithilfe von SQLAlchemy sieht die Abfrage wie folgt aus:
Ignorieren wir Zeile 1 zunächst.
Die Anweisung SQLAlchemy Person.query.order_by(Person.lname).all( )liefert eine Liste von Objekten der Klasse Person, die allen Datensätzen in der Tabelle person entsprechen und nach Nachnamen sortiert sind.
Die Variable people enthält die Liste der Objekte.
Dann iteriert das Programm über die Variable people, indem es nacheinander den Vor- und Nachnamen jeder in der Datenbank vorhandenen Person ausgibt.
Beachte, dass das Programm die Felder fname und lname nicht indiziert, sondern die Attribute der Person-Objekte verwendet.
Die Verwendung von SQLAlchemy ermöglicht es, in Objekten statt in SQL-Abfragen zu denken. Je größer die Datenbank ist, desto praktischer und interessanter ist es.
7. Serialisierung
Die Arbeit mit dem SQLAlchemy-Modell ist in Bezug auf die Programmierung sehr praktisch.
Es ist vor allem praktisch, wenn du Programme schreibst, die Daten manipulieren und Berechnungen mit ihnen durchführen.
Die Anwendung, die wir hier im Auge haben, ist jedoch eine REST-API, die CRUD-Operationen auf Daten bereitstellt und daher nicht mit sehr unterschiedlichen oder komplizierten Datenmanipulationen verbunden ist.
Unsere REST-API arbeitet mit Daten im JSON-Format, was ein Kompatibilitätsproblem mit dem SQLAlchemy-Modell darstellen kann.
Da die von SQLAlchemy zurückgegebenen Daten Python-Objekte (Instanzen von Python-Klassen) sind, kann das Connexion-Modul sie nicht in das JSON-Format umwandeln.
Wir erinnern daran, dass Connexion das Modul ist, das zur Implementierung unserer API verwendet wird, die mithilfe einer YAML-Datei konfiguriert wird.
Serialisierung ist die Umwandlung eines Python-Objekts in einfachere Datenstrukturen, die mithilfe von JSON-Datentypen (JSON datatypes) formatiert werden können; diese sind unten aufgelistet:
string: Zeichenkette
number: von Python zugelassene Zahlen (integers, floats, long)
object: entspricht grob den Python-Wörterbüchern.
array: entspricht grob den Python-Listen.
boolean: nimmt unter JSON die Werte true oder false an, unter Python jedoch True oder False.
null: entspricht None unter Python.
Als Beispiel: Die Klasse Person enthält einen „timestamp“, der ein Python DateTime-Objekt ist.
Es gibt kein Äquivalent unter JSON, also muss das DateTime-Objekt in eine Zeichenkette umgewandelt werden, um unter JSON manipuliert werden zu können.
Die Klasse Person ist eigentlich einfach genug, um in Betracht zu ziehen, die Attribute zu extrahieren und manuell Wörterbücher zu erstellen, die den URLs unserer API entsprechen.
In komplexeren Situationen mit größeren SQLAlchemy-Mustern ist dies jedoch nicht mehr der Fall.
Wir greifen daher auf ein Modul namens Marshmallow zurück, das die Arbeit für uns übernimmt.
8. Marshmallow
Marshmallow ermöglicht es, eine Klasse „PersonSchema“ zu erstellen, die das Gegenstück zur Klasse „Person“ ist, die du unter SQLAlchemy erstellt hast.
Anstatt jedoch die Datenbanktabellen und ihre Felder auf Klassen und ihre Attribute abzubilden, definiert die Klasse PersonSchema, wie die Attribute einer Klasse in ein JSON-Format umgewandelt werden.
Hier ist die Marshmallow-Klassendefinition für die Daten in unserer Tabelle „person“ :
Die Klasse PersonSchema erbt von der Klasse ma.ModelSchema.
Diese ist eine Marshmallow-Basisklasse und bietet eine Reihe von Attributen und Funktionen, die es ermöglichen, Python-Person-Objekte in JSON zu serialisieren und JSON-Daten als Instanzen der Klasse Person zu deserialisieren (die umgekehrte Operation).
Der Rest der Definition läuft wie folgt ab:
class Meta: Definiert eine Klasse mit dem Namen Meta innerhalb unserer Klasse. Die ModelSchema-Klasse, von der PersonSchema erbt, sucht nach dieser internen Meta-Klasse und verwendet sie, um das SQLAlchemy-Modell Person und die Datenbanksitzung db.session zu finden. Dies ermöglicht Marshmallow, die Attribute der Klasse Person zu serialisieren/deserialisieren.
model: Gibt die SQLAlchemy-Vorlage an, die zum Serialisieren/Deserialisieren von Daten verwendet werden soll.
db.session: Gibt die Datenbanksitzung an, die für die Introspektion, d. h. die Untersuchung und Bestimmung von Attributtypen, verwendet werden soll.
9. Erstellen der Datenbank
An diesem Punkt haben wir beschlossen, SQLALchemy zu verwenden, um unsere Datenbank zu manipulieren, was uns erlaubt, uns auf das Datenmodell und die Art und Weise, wie wir sie manipulieren, zu konzentrieren.
Jetzt werden wir unsere Datenbank erstellen.
Dazu werden wir SQLite verwenden.
Wir werden SQLite aus zwei Gründen verwenden:
Sie wird standardmäßig mit Python geliefert und muss daher nicht als separates Modul installiert werden.
Sie speichert alle Informationen in einer einzigen Datei und ist daher einfach einzurichten und zu verwenden.
Es wäre möglich, einen separaten Datenbankserver wie MySQL oder PostgreSQL zu verwenden, aber das würde bedeuten, diese Systeme zu installieren und in Betrieb zu nehmen, was weit über den Zweck dieser Artikelserie hinausgeht.
Da die Datenbank mithilfe von SQLAlchemy manipuliert wird, sind die Details ihrer Implementierung nicht wichtig.
Wir werden ein Programm build_database.py erstellen, um die SQLite-Datenbank people.db, die unsere Daten enthalten wird, zu erstellen und zu initialisieren.
Dabei werden wir zwei Zusatzmodule config.py und models.py erstellen, die von build_database.py und server.py verwendet werden.
config.py: wird sich um den Import und die Konfiguration der verschiedenen benötigten Module kümmern. Dazu gehören Flask, Connection, SQLAlchemy und Marshmallow. Da sie sowohl von build_database.py als auch von server.py verwendet wird, wird ein Teil der Konfiguration nur auf server.py angewendet.
models.py: Dies ist das Modul, das für die Erstellung der Klasse SQLAlchemy Person und der Klasse Marshmallow PersonSchema zuständig ist. Dieses Modul wird über bestimmte Objekte, die in config.py erstellt und konfiguriert wurden, vom Modul config.py abhängen.
Das Konfig-Modul
Das Modul config.py fasst alle Konfigurationsinformationen zusammen.
Hier ist der entsprechende Code :
Die Zeilen 2-4 importieren Connexion, SQLAlchemy und Marshmallow.
Zeile 6 erstellt die Variable basedir, die auf das Verzeichnis zeigt, in dem das Programm ausgeführt wird.
Zeile 9 verwendet die Variable basedir, um die Instanz der Anwendung Connexion zu erstellen, und gibt ihr den Pfad zur Konfigurationsdatei swagger.yml an.
Zeile 12 erstellt eine Variable app, die der Flask-Instanz entspricht, die von Connexion initialisiert wird.
Zeile 15 verwendet die Variable app, um SQLAlchemy zu konfigurieren.
Wir beginnen damit, SQLAlchemy_ECHO auf True zu setzen, was dazu führt, dass die von SQLAlchemy ausgeführten SQL-Abfragen an die Konsole zurückgegeben werden.
Dies ist sehr nützlich in der Entwicklungs- und Debugging-Phase.
In der Produktionsumgebung setzt man sie auf False.
Zeile 19 setzt SQLALCHEMY_DATABASE_URI auf „sqlite:////“ + os.path.join(basedir, „people.db“).
Dies weist SQLALchemy an, SQLite als Datenbank und eine Datei namens people.db im aktuellen Verzeichnis als Datendatei zu verwenden.
Andere Datenbank-Engines, wie MySQL oder PostgreSQL, würden mit einer anderen SQLALCHEMY_DATABASE_URI konfiguriert werden.
Zeile 20 setzt SQLALCHEMY_TRACK_MODIFICATIONS auf False, wodurch das standardmäßig aktive Ereignisbehandlungssystem SQLAlchemy deaktiviert wird.
Dieses System ist nützlich für Programme, die auf Ereignissen (Klicks, …) basieren, verlangsamt aber die Ausführung. Da unser Programm nicht ereignisbasiert ist, schalten wir es aus.
In Zeile 22 wird die Variable db durch Aufruf von SQLAlchemy(app) erstellt.
Dies initialisiert SQLAlchemy mit den Konfigurationsinformationen, die wir gerade angegeben haben. Die Variable db wird in das Programm build_database.py importiert, um ihm Zugriff auf SQLAlchemy und die Datenbank zu geben. Dasselbe wird in den Modulen server.py und people.py geschehen.
Zeile 25 erstellt die Variable ma durch einen Aufruf von Marshmallow(app). Dies initialisiert Marshmallow und erlaubt die Introspektion von SQLAlchemy-Komponenten, die an die Datenbank angehängt sind. Marshmallow muss daher nach SQLAlchemy initialisiert werden.
Das Models-Modul
Das models.py-Modul wird verwendet, um die Klassen SQLAlchemy Person und Marshmallow PersonSchema zu definieren, wie zuvor gesehen.
Hier ist der entsprechende Code:
Zeile 1 importiert die Klasse datetime aus dem Modul datetime, das standardmäßig in Python verfügbar ist.
Zeile 2 importiert die Objekte db und ma aus dem Modul config.py. Dadurch erhält das Programm Zugriff auf die SQLAlchemy-Attribute und Methoden, die an das db-Objekt angehängt sind, sowie auf die Marshmallow-Attribute und Methoden, die an das ma-Objekt angehängt sind.
Die Zeilen 4-11 definieren die Klasse Person so, dass sie die SQLAlchemy-Funktionen nutzen kann, wie z. B. die Verbindung zu einer Datenbank und den Zugriff auf ihre Tabellen.
Die Zeilen 14-17 definieren die Klasse PersonSchema so, dass sie von den Marshmallow-Funktionen profitiert. So ermöglicht die Introspektion auf die Klasse Person die Serialisierung/Deserialisierung der Instanzen dieser Klasse.
Erstellen einer Datenbank
Wir haben gesehen, wie Tabellen in einer Datenbank mit SQLAlchemy-Klassen abgebildet werden können.
Jetzt werden wir die Datenbank erstellen und ihr Daten zuweisen.
Dazu verwenden wir das folgende Programm build_database.py:
Zeile 2 importiert das db-Objekt aus dem Modul config.py.
Zeile 3 importiert die Klasse Person aus dem Modul models.py.
Die Zeilen 6 bis 10 erstellen die Struktur PEOPLE, die eine Liste von Wörterbüchern ist, die die Daten enthalten. Beachte, dass eine solche Struktur leicht z. B. aus einer CSV-Datei erstellt werden kann.
Die Zeilen 13 und 14 räumen ein wenig auf, indem sie die Datei people.db löschen, falls sie bereits existiert. Dadurch wird sichergestellt, dass du bei Null anfängst, wenn du die Datenbank neu erstellen musst.
Zeile 17 erstellt die Datenbank mithilfe des Aufrufs db.create_all( ). Dies erstellt die Datenbank unter Verwendung der db-Instanz, die aus dem config-Modul importiert wurde. Die db-Instanz ist unsere Verbindung zur Datenbank.
Die Zeilen 20 bis 22 iterieren über die PEOPLE-Liste und verwenden die darin enthaltenen Wörterbücher, um die Klasse Person zu instanziieren.
Nachdem diese instanziiert wurde, rufen wir die Funktion db.session.add(p) auf.
Die Verbindungsinstanz db wird verwendet, um auf das Session-Objekt zuzugreifen.
Die Sitzung ist das, was die Aktionen verwaltet, die in der Datenbank ausgeführt werden.
Hier führen wir die Methode add(p) aus, um eine neue Instanz von Person zum Session-Objekt hinzuzufügen.
In Zeile 24 wird db.session.commit( ) aufgerufen, um alle erstellten Person-Objekte in der Datenbank zu speichern.
In Zeile 22 ist noch nichts in der Datenbank gespeichert; alles wird im Session-Objekt gespeichert. Erst nach der Ausführung von db.session.commit( ) interagiert die Sitzung mit der Datenbank und wendet die angegebenen Aktionen auf sie an.
Unter SQLAlchemy ist die Sitzung ein wichtiges Objekt. Sie verbindet die Datenbank und die SQLAlchemy-Objekte, die in einem Python-Programm manipuliert werden.
Sie sorgt dafür, dass die Daten im Programm und die Daten in der Datenbank konsistent bleiben.
Alle ausgeführten Aktionen werden in der Datenbank gespeichert und sie aktualisiert die Datenbank entsprechend den expliziten oder impliziten Aktionen, die im Programm ausgeführt werden.
Jetzt können wir die Datenbank erstellen und initialisieren.
Führe einfach das Programm build_database.py aus, z. B. in Spyder.
Wenn das Programm ausgeführt wird, zeigt es die Meldungen von SQLAlchemy auf der Konsole an. Das liegt daran, dass wir SQLALCHEMY_ECHO im config.py-Modul auf True gesetzt haben.
Das meiste, was angezeigt wird, sind die von SQLAlchemy erzeugten SQL-Abfragen, um die Datenbankdatei people.db zu erstellen und zu füllen.
Wir werden im nächsten Artikel sehen, wie wir unsere API mit all den Werkzeugen, die uns jetzt zur Verfügung stehen, aktualisieren können.
10. Bibliografische Referenzen
- Creating Web APIs with Python and Flask, Patrick Smyth, 2022 : https://programminghistorian.org/en/lessons/creating-apis-with-python-and-flask.
- Python REST APIs With Flask, Connexion, and SQLAlchemy, Doug Farrell, 2022 : https://realpython.com/flask-connexion-rest-api/.
- Python REST APIs With Flask, Connexion, and SQLAlchemy, Doug Farrell, 2022 : https://realpython.com/flask-connexion-rest-api-part-2/.
- Flask RESTful documentation, 2020 : https://flask-restful.readthedocs.io/en/latest/index.html.
- Flask Web Development : Developing Web Applications with Python (2ème édition). M. Grinberg. O’Reilly 2018.
- Architectural Styles and the Design of Network-Based Software Architectures. T. Fielding. Thèse, University of California, 2000.