Tabelle mit SQLModel erstellen - Engine verwenden¶
Jetzt kommen wir zum Code. 👩💻
Stellen Sie sicher, dass Sie sich in Ihrem Projektverzeichnis befinden und Ihre virtuelle Umgebung aktiviert haben, wie in Virtuelle Umgebungen erklärt.
Wir werden
- Eine Tabelle mit SQLModel definieren
- Dieselbe SQLite-Datenbank und Tabelle mit SQLModel erstellen
- DB Browser for SQLite verwenden, um die Operationen zu bestätigen
Hier ist eine Erinnerung an die Tabellenstruktur, die wir wollen
| id | name | secret_name | age |
|---|---|---|---|
| 1 | Deadpond | Dive Wilson | null |
| 2 | Spider-Boy | Pedro Parqueador | null |
| 3 | Rusty-Man | Tommy Sharp | 48 |
Modellklasse für die Tabelle erstellen¶
Das Erste, was wir tun müssen, ist eine Klasse zu erstellen, die die Daten in der Tabelle repräsentiert.
Eine Klasse wie diese, die Daten repräsentiert, wird üblicherweise als Modell bezeichnet.
Tipp
Deshalb heißt dieses Paket SQLModel. Weil es hauptsächlich zum Erstellen von SQL-Modellen verwendet wird.
Dazu importieren wir SQLModel (sowie andere Dinge, die wir ebenfalls verwenden werden) und erstellen eine Klasse Hero, die von SQLModel erbt und das Tabellenmodell für unsere Helden repräsentiert.
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
# Code below omitted 👇
👀 Vollständige Dateivorschau
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
🤓 Andere Versionen und Varianten
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
Diese Klasse Hero repräsentiert die Tabelle für unsere Helden. Und jede Instanz, die wir später erstellen, wird eine Zeile in der Tabelle repräsentieren.
Wir verwenden die Konfiguration table=True, um SQLModel mitzuteilen, dass dies ein Tabellenmodell ist, das eine Tabelle repräsentiert.
Info
Es ist auch möglich, Modelle ohne table=True zu haben, das wären nur Datenmodelle, ohne eine Tabelle in der Datenbank, sie wären keine Tabellenmodelle.
Diese Datenmodelle werden später sehr nützlich sein, aber vorerst behalten wir die Konfiguration table=True bei.
Felder, Spalten definieren¶
Der nächste Schritt ist die Definition der Felder oder Spalten der Klasse unter Verwendung von Standard-Python-Typ-Annotationen.
Der Name jeder dieser Variablen wird der Name der Spalte in der Tabelle sein.
Und der Typ jeder einzelnen wird ebenfalls der Typ der Tabellenspalte sein.
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
# Code below omitted 👇
👀 Vollständige Dateivorschau
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
🤓 Andere Versionen und Varianten
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
Betrachten wir nun diese Feld-/Spaltendefinitionen genauer.
None Felder, Nullable Spalten¶
Beginnen wir mit age, beachten Sie, dass es den Typ int | None hat.
Das ist die Standardmethode, um in Python zu deklarieren, dass etwas "int oder None sein könnte".
Und wir setzen auch den Standardwert von age auf None.
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
# Code below omitted 👇
👀 Vollständige Dateivorschau
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
🤓 Andere Versionen und Varianten
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
Tipp
Wir definieren id auch mit int | None. Aber wir werden id unten besprechen.
Da der Typ int | None ist
- Bei der Datenvalidierung wird
Noneein zulässiger Wert füragesein. - In der Datenbank darf die Spalte für
ageNULL(das SQL-Äquivalent zu PythonsNone) enthalten.
Und da ein Standardwert = None vorhanden ist
- Bei der Datenvalidierung ist dieses Feld
agenicht erforderlich, es wird standardmäßigNonesein. - Beim Speichern in der Datenbank hat die Spalte
agestandardmäßig einenNULL-Wert.
Tipp
Der Standardwert könnte auch etwas anderes gewesen sein, z. B. = 42.
Primärschlüssel id¶
Betrachten wir nun das Feld id. Dies ist der Primärschlüssel der Tabelle.
Daher müssen wir id als Primärschlüssel markieren.
Dazu verwenden wir die spezielle Funktion Field von sqlmodel und setzen das Argument primary_key=True.
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
# Code below omitted 👇
👀 Vollständige Dateivorschau
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
🤓 Andere Versionen und Varianten
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
Dadurch teilen wir SQLModel mit, dass dieses Feld/diese Spalte id der Primärschlüssel der Tabelle ist.
Aber innerhalb der SQL-Datenbank ist er immer erforderlich und kann nicht NULL sein. Warum sollten wir ihn mit int | None deklarieren?
Die id wird in der Datenbank erforderlich sein, aber sie wird von der Datenbank generiert, nicht von unserem Code.
Wenn wir also eine Instanz dieser Klasse erstellen (in den nächsten Kapiteln), werden wir die id nicht setzen. Und der Wert von id wird None sein, bis wir sie in der Datenbank speichern, und dann wird sie endlich einen Wert haben.
my_hero = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
do_something(my_hero.id) # Oh no! my_hero.id is None! 😱🚨
# Imagine this saves it to the database
somehow_save_in_db(my_hero)
do_something(my_hero.id) # Now my_hero.id has a value generated in DB 🎉
Da also in unserem Code (nicht in der Datenbank) der Wert von id None sein könnte, verwenden wir int | None. So wird uns der Editor helfen können, zum Beispiel, wenn wir versuchen, auf die id eines Objekts zuzugreifen, das wir noch nicht in der Datenbank gespeichert haben und das immer noch None wäre.

Da wir nun den Platz des Standardwerts mit unserer Field()-Funktion einnehmen, setzen wir mit dem Argument default=None in Field() den tatsächlichen Standardwert von id auf None.
Field(default=None)
Wenn wir den default-Wert nicht setzen würden, würde er, sobald wir dieses Modell später für die Datenvalidierung (gestützt durch Pydantic) verwenden, neben einem int auch einen Wert von None *akzeptieren*, aber die Übergabe dieses None-Werts erfordern. Und das wäre verwirrend für jeden, der dieses Modell später benutzt (wahrscheinlich wir selbst), also ist es besser, den Standardwert hier zu setzen.
Engine erstellen¶
Jetzt müssen wir die SQLAlchemy Engine erstellen.
Es ist ein Objekt, das die Kommunikation mit der Datenbank handhabt.
Wenn Sie eine Server-Datenbank haben (z. B. PostgreSQL oder MySQL), hält die Engine die Netzwerkverbindungen zu dieser Datenbank.
Das Erstellen der Engine ist sehr einfach, rufen Sie einfach create_engine() mit einer URL für die zu verwendende Datenbank auf.
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
👀 Vollständige Dateivorschau
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
🤓 Andere Versionen und Varianten
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
Sie sollten normalerweise ein einziges Engine-Objekt für Ihre gesamte Anwendung haben und es überall wiederverwenden.
Tipp
Es gibt noch etwas anderes, das als Session bezeichnet wird und normalerweise kein einzelnes Objekt pro Anwendung sein sollte.
Aber dazu werden wir später sprechen.
Engine Datenbank-URL¶
Jede unterstützte Datenbank hat ihren eigenen URL-Typ. Für SQLite ist das zum Beispiel sqlite:/// gefolgt vom Dateipfad. Zum Beispiel
sqlite:///database.dbsqlite:///databases/local/application.dbsqlite:///db.sqlite
SQLite unterstützt eine spezielle Datenbank, die komplett *im Speicher* lebt. Daher ist sie sehr schnell, aber seien Sie vorsichtig, die Datenbank wird nach Beendigung des Programms gelöscht. Sie können diese In-Memory-Datenbank angeben, indem Sie nur zwei Schrägstriche (//) und keinen Dateinamen verwenden.
sqlite://
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
👀 Vollständige Dateivorschau
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
🤓 Andere Versionen und Varianten
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
Sie können viel mehr über alle von SQLAlchemy unterstützten Datenbanken (und damit von SQLModel unterstützten) in der SQLAlchemy-Dokumentation lesen.
Engine Echo¶
In diesem Beispiel verwenden wir auch das Argument echo=True.
Dadurch gibt die Engine alle SQL-Anweisungen aus, die sie ausführt, was Ihnen helfen kann zu verstehen, was passiert.
Es ist besonders nützlich zum Lernen und Debuggen.
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
👀 Vollständige Dateivorschau
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
🤓 Andere Versionen und Varianten
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
Aber in der Produktion würden Sie wahrscheinlich echo=True entfernen wollen.
engine = create_engine(sqlite_url)
Engine Technische Details¶
Tipp
Wenn Sie vorher nichts über SQLAlchemy wussten und nur SQLModel lernen, können Sie diesen Abschnitt wahrscheinlich überspringen und nach unten scrollen.
Sie können viel mehr über die Engine in der SQLAlchemy-Dokumentation lesen.
SQLModel definiert seine eigene Funktion create_engine(). Sie ist dieselbe wie die create_engine() von SQLAlchemy, mit dem Unterschied, dass sie standardmäßig future=True verwendet (was bedeutet, dass sie den Stil des neuesten SQLAlchemy, 1.4, und des zukünftigen 2.0 verwendet).
Und die Version von create_engine() von SQLModel ist intern typisiert, sodass Ihr Editor Ihnen mit Autovervollständigung und Inline-Fehlern helfen kann.
Datenbank und Tabelle erstellen¶
Jetzt ist alles vorbereitet, um endlich die Datenbank und die Tabelle zu erstellen.
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
🤓 Andere Versionen und Varianten
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
Tipp
Das Erstellen der Engine erstellt noch nicht die Datei database.db.
Aber sobald wir SQLModel.metadata.create_all(engine) ausführen, erstellt es die Datei database.db und erstellt die Tabelle hero in dieser Datenbank.
Beides geschieht in diesem einzigen Schritt.
Lassen Sie uns das aufschlüsseln.
SQLModel.metadata.create_all(engine)
SQLModel MetaData¶
Die Klasse SQLModel hat ein Attribut metadata. Es ist eine Instanz der Klasse MetaData.
Immer wenn Sie eine Klasse erstellen, die von SQLModel erbt und mit table = True konfiguriert ist, wird sie in diesem metadata-Attribut registriert.
Daher hat SQLModel.metadata durch die letzte Zeile die Registrierung von Hero bereits abgeschlossen.
create_all() aufrufen¶
Dieses Objekt MetaData unter SQLModel.metadata hat eine Methode create_all().
Es nimmt eine Engine und verwendet diese, um die Datenbank und alle in diesem MetaData-Objekt registrierten Tabellen zu erstellen.
SQLModel MetaData Reihenfolge ist wichtig¶
Das bedeutet auch, dass Sie SQLModel.metadata.create_all() nach dem Code aufrufen müssen, der neue Modellklassen erstellt, die von SQLModel erben.
Stellen Sie sich zum Beispiel vor, Sie tun Folgendes
- Erstellen Sie die Modelle in einer Python-Datei
models.py. - Erstellen Sie das Engine-Objekt in einer Datei
db.py. - Erstellen Sie Ihre Hauptanwendung und rufen Sie
SQLModel.metadata.create_all()inapp.pyauf.
Wenn Sie nur SQLModel importieren und versuchen würden, SQLModel.metadata.create_all() in app.py aufzurufen, würden Ihre Tabellen nicht erstellt werden.
# This wouldn't work! 🚨
from sqlmodel import SQLModel
from .db import engine
SQLModel.metadata.create_all(engine)
Es würde nicht funktionieren, weil, wenn Sie nur SQLModel importieren, Python nicht den gesamten Code ausführt, der die Klassen erstellt, die davon erben (in unserem Beispiel die Klasse Hero), daher ist SQLModel.metadata noch leer.
Aber wenn Sie die Modelle bevor Sie SQLModel.metadata.create_all() aufrufen, importieren, wird es funktionieren.
from sqlmodel import SQLModel
from . import models
from .db import engine
SQLModel.metadata.create_all(engine)
Dies würde funktionieren, weil durch den Import der Modelle Python den gesamten Code ausführt, der die Klassen erstellt, die von SQLModel erben, und sie im SQLModel.metadata registriert.
Alternativ könnten Sie SQLModel und Ihre Modelle in db.py importieren.
# db.py
from sqlmodel import SQLModel, create_engine
from . import models
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url)
Und dann SQLModel *von* db.py in app.py importieren und dort SQLModel.metadata.create_all() aufrufen.
# app.py
from .db import engine, SQLModel
SQLModel.metadata.create_all(engine)
Der Import von SQLModel aus db.py würde funktionieren, weil SQLModel auch in db.py importiert wird.
Und dieser Trick würde korrekt funktionieren und die Tabellen in der Datenbank erstellen, weil durch den Import von SQLModel aus db.py Python den gesamten Code ausführt, der die Klassen erstellt, die von SQLModel in dieser db.py-Datei erben, z. B. die Klasse Hero.
Migrationen¶
Für dieses einfache Beispiel und für den größten Teil des Tutorials - Benutzerhandbuch reicht die Verwendung von SQLModel.metadata.create_all() aus.
Aber für ein Produktionssystem würden Sie wahrscheinlich ein System zur Migration der Datenbank verwenden wollen.
Dies wäre nützlich und wichtig, zum Beispiel, wenn Sie eine Spalte hinzufügen oder entfernen, eine neue Tabelle hinzufügen, einen Typ ändern usw.
Aber über Migrationen werden Sie später im Fortgeschrittenen Benutzerhandbuch erfahren.
Das Programm ausführen¶
Lassen Sie uns das Programm ausführen, um alles funktionsfähig zu sehen.
Speichern Sie den Code, falls noch nicht geschehen, in einer Datei app.py.
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
🤓 Andere Versionen und Varianten
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
SQLModel.metadata.create_all(engine)
Tipp
Denken Sie daran, die virtuelle Umgebung zu aktivieren, bevor Sie sie ausführen.
Führen Sie nun das Programm mit Python aus.
// We set echo=True, so this will show the SQL code
$ python app.py
// First, some boilerplate SQL that we are not that interested in
INFO Engine BEGIN (implicit)
INFO Engine PRAGMA main.table_info("hero")
INFO Engine [raw sql] ()
INFO Engine PRAGMA temp.table_info("hero")
INFO Engine [raw sql] ()
INFO Engine
// Finally, the glorious SQL to create the table ✨
CREATE TABLE hero (
id INTEGER,
name VARCHAR NOT NULL,
secret_name VARCHAR NOT NULL,
age INTEGER,
PRIMARY KEY (id)
)
// More SQL boilerplate
INFO Engine [no key 0.00020s] ()
INFO Engine COMMIT
Info
Ich habe die Ausgabe oben etwas vereinfacht, um sie leichter lesbar zu machen.
Aber in Wirklichkeit würde es anstelle der Anzeige von
INFO Engine BEGIN (implicit)
etwas wie dies anzeigen
2021-07-25 21:37:39,175 INFO sqlalchemy.engine.Engine BEGIN (implicit)
TEXT oder VARCHAR¶
Im Beispiel des vorherigen Kapitels haben wir die Tabelle mit TEXT für einige Spalten erstellt.
Aber in dieser Ausgabe verwendet SQLAlchemy stattdessen VARCHAR. Lassen Sie uns sehen, was los ist.
Erinnern Sie sich, dass jede SQL-Datenbank Unterschiede in dem hat, was sie unterstützt?
Dies ist einer der Unterschiede. Jede Datenbank unterstützt bestimmte Datentypen wie INTEGER und TEXT.
Einige Datenbanken haben bestimmte Typen, die für bestimmte Dinge besonders sind. Zum Beispiel unterstützen PostgreSQL und MySQL BOOLEAN für die Werte True und False. SQLite akzeptiert SQL mit Booleans, auch beim Definieren von Tabellenspalten, aber intern verwendet es INTEGERs, wobei 1 für True und 0 für False steht.
Auf die gleiche Weise gibt es verschiedene mögliche Typen zur Speicherung von Zeichenketten. SQLite verwendet den Typ TEXT. Andere Datenbanken wie PostgreSQL und MySQL verwenden jedoch standardmäßig den Typ VARCHAR, und VARCHAR ist einer der gebräuchlichsten Datentypen.
VARCHAR kommt von variable length character (variable Längen Zeichen).
SQLAlchemy generiert die SQL-Anweisungen zum Erstellen von Tabellen mit VARCHAR, und dann empfängt SQLite diese und wandelt sie intern in TEXTs um.
Zusätzlich zu den Unterschieden zwischen diesen beiden Datentypen verlangen einige Datenbanken wie MySQL die Angabe einer maximalen Länge für die VARCHAR-Typen, z. B. setzt VARCHAR(255) die maximale Anzahl von Zeichen auf 255.
Um den Einstieg in SQLModel unabhängig von der verwendeten Datenbank (auch mit MySQL) und ohne zusätzliche Konfigurationen zu erleichtern, werden str-Felder standardmäßig in den meisten Datenbanken als VARCHAR und in MySQL als VARCHAR(255) interpretiert. So wissen Sie, dass dieselbe Klasse ohne zusätzlichen Aufwand mit den gängigsten Datenbanken kompatibel sein wird.
Tipp
Wie Sie die maximale Länge von Zeichenketten-Spalten ändern können, erfahren Sie später im Fortgeschrittenen Tutorial - Benutzerhandbuch.
Datenbank überprüfen¶
Öffnen Sie nun die Datenbank mit DB Browser for SQLite, Sie werden sehen, dass das Programm die Tabelle hero genau wie zuvor erstellt hat. 🎉

Datenerstellung refaktorisieren¶
Lassen Sie uns nun den Code etwas umstrukturieren, um ihn später leichter wiederverwenden, teilen und testen zu können.
Verschieben wir den Code, der die Haupt-Nebeneffekte hat, der Daten verändert (erstellt eine Datei mit einer Datenbank und einer Tabelle), in eine Funktion.
In diesem Beispiel ist es nur SQLModel.metadata.create_all(engine).
Lassen Sie uns das in eine Funktion create_db_and_tables() packen.
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
# Code below omitted 👇
👀 Vollständige Dateivorschau
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
if __name__ == "__main__":
create_db_and_tables()
🤓 Andere Versionen und Varianten
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
if __name__ == "__main__":
create_db_and_tables()
Wenn SQLModel.metadata.create_all(engine) nicht in einer Funktion wäre und wir versuchen würden, etwas aus diesem Modul (aus dieser Datei) in einem anderen zu importieren, würde es versuchen, die Datenbank und die Tabelle jedes Mal zu erstellen, wenn wir diese andere Datei ausführen, die dieses Modul importiert.
Das wollen wir nicht, dass es so passiert, sondern nur, wenn wir es *absichtlich* tun, deshalb packen wir es in eine Funktion, weil wir sicherstellen können, dass die Tabellen nur erstellt werden, wenn wir diese Funktion aufrufen, und nicht, wenn dieses Modul woanders importiert wird.
Jetzt könnten wir zum Beispiel die Klasse Hero in einer anderen Datei importieren, ohne diese Nebeneffekte zu haben.
Tipp
😅 Spoiler-Alarm: Die Funktion heißt create_db_and_tables(), weil wir in Zukunft mehr Tabellen mit anderen Klassen neben Hero haben werden. 🚀
Daten als Skript erstellen¶
Wir haben die Nebeneffekte beim Importieren von etwas aus Ihrer app.py-Datei verhindert.
Aber wir möchten immer noch, dass sie die Datenbank und die Tabelle erstellt, wenn wir sie direkt mit Python als unabhängiges Skript vom Terminal aus aufrufen, genau wie oben.
Tipp
Betrachten Sie die Wörter Skript und Programm als austauschbar.
Das Wort Skript impliziert oft, dass der Code unabhängig und einfach ausgeführt werden kann. Oder in einigen Fällen bezieht es sich auf ein relativ einfaches Programm.
Dazu können wir die spezielle Variable __name__ in einem if-Block verwenden.
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
if __name__ == "__main__":
create_db_and_tables()
🤓 Andere Versionen und Varianten
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
if __name__ == "__main__":
create_db_and_tables()
Über __name__ == "__main__"¶
Der Hauptzweck von __name__ == "__main__" ist es, Code zu haben, der ausgeführt wird, wenn Ihre Datei mit
$ python app.py
// Something happens here ✨
...aber nicht aufgerufen wird, wenn eine andere Datei sie importiert, wie in
from app import Hero
Tipp
Dieser if-Block, der if __name__ == "__main__": verwendet, wird manchmal als "Hauptblock" bezeichnet.
Der offizielle Name (in der Python-Dokumentation) ist "Top-Level-Skriptumgebung".
Mehr Details¶
Nehmen wir an, Ihre Datei heißt myapp.py.
Wenn Sie sie mit
$ python myapp.py
// This will call create_db_and_tables()
...dann wird die interne Variable __name__ in Ihrer Datei, die automatisch von Python erstellt wird, den Wert des Strings "__main__" haben.
So wird die Funktion in
if __name__ == "__main__":
create_db_and_tables()
...ausgeführt.
Das passiert nicht, wenn Sie dieses Modul (Datei) importieren.
Wenn Sie also eine andere Datei importer.py mit
from myapp import Hero
# Some more code
...in diesem Fall hat die automatische Variable in myapp.py nicht die Variable __name__ mit dem Wert "__main__".
Daher wird die Zeile
if __name__ == "__main__":
create_db_and_tables()
...nicht ausgeführt.
Info
Weitere Informationen finden Sie in der offiziellen Python-Dokumentation.
Letzte Überprüfung¶
Nach diesen Änderungen könnten Sie sie erneut ausführen, und sie würde die gleiche Ausgabe wie zuvor erzeugen.
Aber jetzt können wir Dinge aus diesem Modul in anderen Dateien importieren.
Werfen wir nun einen letzten Blick auf den Code.
from sqlmodel import Field, SQLModel, create_engine # (2)!
class Hero(SQLModel, table=True): # (3)!
id: int | None = Field(default=None, primary_key=True) # (4)!
name: str # (5)!
secret_name: str # (6)!
age: int | None = None # (7)!
sqlite_file_name = "database.db" # (8)!
sqlite_url = f"sqlite:///{sqlite_file_name}" # (9)!
engine = create_engine(sqlite_url, echo=True) # (10)!
def create_db_and_tables(): # (11)!
SQLModel.metadata.create_all(engine) # (12)!
if __name__ == "__main__": # (13)!
create_db_and_tables() # (14)!
- Importieren Sie
Optionalaustyping, um Felder zu deklarieren, dieNonesein können. - Importieren Sie die Dinge, die wir von
sqlmodelbenötigen:Field,SQLModel,create_engine. -
Erstellen Sie die Modellklasse
Hero, die die Tabelleheroin der Datenbank repräsentiert.Und markieren Sie diese Klasse auch als Tabellenmodell mit
table=True. -
Erstellen Sie das Feld
id.Es könnte
Nonesein, bis die Datenbank ihm einen Wert zuweist, daher annotieren wir es mitOptional.Es ist ein Primärschlüssel, daher verwenden wir
Field()und das Argumentprimary_key=True. -
Erstellen Sie das Feld
name.Es ist erforderlich, daher gibt es keinen Standardwert und es ist nicht
Optional. -
Erstellen Sie das Feld
secret_name.Ebenfalls erforderlich.
-
Erstellen Sie das Feld
age.Es ist nicht erforderlich, der Standardwert ist
None.In der Datenbank ist der Standardwert
NULL, das SQL-Äquivalent zuNone.Da dieses Feld
None(und in der DatenbankNULL) sein kann, annotieren wir es mitOptional. -
Schreiben Sie den Namen der Datenbankdatei.
- Verwenden Sie den Namen der Datenbankdatei, um die Datenbank-URL zu erstellen.
-
Erstellen Sie die Engine mit der URL.
Dies erstellt die Datenbank noch nicht. Zu diesem Zeitpunkt wird keine Datei oder Tabelle erstellt, nur das Engine-Objekt, das die Verbindungen zu dieser spezifischen Datenbank verwaltet, und mit spezifischer Unterstützung für SQLite (basierend auf der URL).
-
Packen Sie den Code, der Nebeneffekte verursacht, in eine Funktion.
In diesem Fall nur eine Zeile, die die Datenbankdatei mit der Tabelle erstellt.
-
Erstellen Sie alle Tabellen, die automatisch in
SQLModel.metadataregistriert wurden. -
Fügen Sie einen Hauptblock oder eine "Top-Level-Skriptumgebung" hinzu.
Und fügen Sie einige Logik hinzu, die ausgeführt werden soll, wenn dies direkt mit Python aufgerufen wird, wie in
$ python app.py // Execute all the stuff and show the output...aber die nicht ausgeführt wird, wenn etwas aus diesem Modul importiert wird, wie
from app import Hero -
Rufen Sie in diesem Hauptblock die Funktion auf, die die Datenbankdatei und die Tabelle erstellt.
Auf diese Weise, wenn wir es mit
$ python app.py // Doing stuff ✨...wird die Datenbankdatei und die Tabelle erstellt.
from typing import Optional # (1)!
from sqlmodel import Field, SQLModel, create_engine # (2)!
class Hero(SQLModel, table=True): # (3)!
id: Optional[int] = Field(default=None, primary_key=True) # (4)!
name: str # (5)!
secret_name: str # (6)!
age: Optional[int] = None # (7)!
sqlite_file_name = "database.db" # (8)!
sqlite_url = f"sqlite:///{sqlite_file_name}" # (9)!
engine = create_engine(sqlite_url, echo=True) # (10)!
def create_db_and_tables(): # (11)!
SQLModel.metadata.create_all(engine) # (12)!
if __name__ == "__main__": # (13)!
create_db_and_tables() # (14)!
- Importieren Sie
Optionalaustyping, um Felder zu deklarieren, dieNonesein können. - Importieren Sie die Dinge, die wir von
sqlmodelbenötigen:Field,SQLModel,create_engine. -
Erstellen Sie die Modellklasse
Hero, die die Tabelleheroin der Datenbank repräsentiert.Und markieren Sie diese Klasse auch als Tabellenmodell mit
table=True. -
Erstellen Sie das Feld
id.Es könnte
Nonesein, bis die Datenbank ihm einen Wert zuweist, daher annotieren wir es mitOptional.Es ist ein Primärschlüssel, daher verwenden wir
Field()und das Argumentprimary_key=True. -
Erstellen Sie das Feld
name.Es ist erforderlich, daher gibt es keinen Standardwert und es ist nicht
Optional. -
Erstellen Sie das Feld
secret_name.Ebenfalls erforderlich.
-
Erstellen Sie das Feld
age.Es ist nicht erforderlich, der Standardwert ist
None.In der Datenbank ist der Standardwert
NULL, das SQL-Äquivalent zuNone.Da dieses Feld
None(und in der DatenbankNULL) sein kann, annotieren wir es mitOptional. -
Schreiben Sie den Namen der Datenbankdatei.
- Verwenden Sie den Namen der Datenbankdatei, um die Datenbank-URL zu erstellen.
-
Erstellen Sie die Engine mit der URL.
Dies erstellt die Datenbank noch nicht. Zu diesem Zeitpunkt wird keine Datei oder Tabelle erstellt, nur das Engine-Objekt, das die Verbindungen zu dieser spezifischen Datenbank verwaltet, und mit spezifischer Unterstützung für SQLite (basierend auf der URL).
-
Packen Sie den Code, der Nebeneffekte verursacht, in eine Funktion.
In diesem Fall nur eine Zeile, die die Datenbankdatei mit der Tabelle erstellt.
-
Erstellen Sie alle Tabellen, die automatisch in
SQLModel.metadataregistriert wurden. -
Fügen Sie einen Hauptblock oder eine "Top-Level-Skriptumgebung" hinzu.
Und fügen Sie einige Logik hinzu, die ausgeführt werden soll, wenn dies direkt mit Python aufgerufen wird, wie in
$ python app.py // Execute all the stuff and show the output...aber die nicht ausgeführt wird, wenn etwas aus diesem Modul importiert wird, wie
from app import Hero -
Rufen Sie in diesem Hauptblock die Funktion auf, die die Datenbankdatei und die Tabelle erstellt.
Auf diese Weise, wenn wir es mit
$ python app.py // Doing stuff ✨...wird die Datenbankdatei und die Tabelle erstellt.
Tipp
Überprüfen Sie, was jede Zeile tut, indem Sie auf jede Zahlenblase im Code klicken. 👆
Zusammenfassung¶
Wir haben gelernt, wie man SQLModel verwendet, um zu definieren, wie eine Tabelle in der Datenbank aussehen soll, und wir haben mit SQLModel eine Datenbank und eine Tabelle erstellt.
Wir haben auch den Code refaktorisiert, um ihn leichter wiederverwenden, teilen und später testen zu können.
In den nächsten Kapiteln werden wir sehen, wie SQLModel uns helfen wird, mit SQL-Datenbanken aus Code zu interagieren. 🤓