DokuEinheit 2: Daten
Erstellen Sie eine Notizen-App mit Nexy, Teil 5: Speichern Sie die Notizen in SQLite
Teil 5 verschiebt die Notizen aus einem Python-Wörterbuch in eine SQLite-Datenbank, mit einem Modell, einer Migration und zunächst zwei Notizen, und die Seiten lesen sie von dort aus.
Bevor Sie beginnen: die App von Teil 4.
Installieren Sie den ORM und das Migrationstool
uv add sqlmodel alembic
SQLModel beschreibt eine Tabelle als eine Python-Klasse, und Alembic wandelt Änderungen an diesen Klassen in Migrationen um. Nexy verpackt auch nicht: Sie verwenden sie so, wie sie sind.
Öffnen Sie die Datenbank
Die Adresse der Datenbank befindet sich in .env im Stammverzeichnis des Projekts. nx migrate und die App haben es beide von dort gelesen:
DATABASE_URL=sqlite:///dev.dbsqlite:///dev.db ist eine Datei, dev.db, neben nexyconfig.py: SQLite benötigt keinen Server. Halten Sie .env von Git fern: Es birgt die Geheimnisse einer echten Datenbank.
Die Engine ist die Verbindung zur Datenbank. Es lautet DATABASE_URL, mit derselben Datei als Standard, wenn die Variable nicht gesetzt ist:
import os from dotenv import load_dotenvfrom sqlmodel import create_engine load_dotenv() engine = create_engine(os.getenv("DATABASE_URL", "sqlite:///dev.db"))Beschreiben Sie eine Notiz
from sqlmodel import Field, SQLModel class Note(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) text: strDas Modell lebt mit der Funktion, zu der es gehört, neben seinem Controller:
from sqlmodel import Field, SQLModel class Note(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) text: strtable=True macht die Klasse zu einer Tabelle, note. Der id wird von der Datenbank vergeben, wenn eine Notiz gespeichert wird.
Erstellen Sie die Tabelle mit einer Migration
nx migrate --initnx migrate -m "create notes"nx migrate --upgrade
--init schreibt den Ordner alembic/ und zeigt ihn auf Ihr Modell. -m vergleicht das Modell mit der Datenbank und schreibt die Migration, die die Tabelle erstellt; --upgrade führt es aus. Ändern Sie das Modell später, führen Sie die letzten beiden Befehle erneut aus und die Tabelle folgt.
Lesen Sie die Notizen aus der Datenbank
Die Funktionen von src/notes.py behalten ihre Namen, daher ändern sich die Seiten kaum: Sie öffnen jetzt eine Sitzung, fragen die Tabelle ab und geben Note-Objekte anstelle von Zeichenfolgen zurück.
from sqlmodel import Session, select from src.configs.db import enginefrom src.models.note_model import Note def all_notes() -> list[Note]: with Session(engine) as session: return list(session.exec(select(Note).order_by(Note.id))) def find(note_id: int) -> Note | None: with Session(engine) as session: return session.get(Note, note_id)from sqlmodel import Session, select from src.apps.notes.note_model import Notefrom src.configs.db import engine def all_notes() -> list[Note]: with Session(engine) as session: return list(session.exec(select(Note).order_by(Note.id))) def find(note_id: int) -> Note | None: with Session(engine) as session: return session.get(Note, note_id)Fügen Sie zunächst zwei Notizen hinzu
Ein Seed-Skript füllt eine leere Datenbank, sodass jede neue Kopie des Projekts mit etwas beginnt, das angezeigt werden soll:
from sqlmodel import Session, select from src.configs.db import enginefrom src.models.note_model import Note def seed() -> None: with Session(engine) as session: if session.exec(select(Note)).first() is None: session.add_all([Note(text="Buy milk"), Note(text="Ship Nexy")]) session.commit() if __name__ == "__main__": seed()from sqlmodel import Session, select from src.apps.notes.note_model import Notefrom src.configs.db import engine def seed() -> None: with Session(engine) as session: if session.exec(select(Note)).first() is None: session.add_all([Note(text="Buy milk"), Note(text="Ship Nexy")]) session.commit() if __name__ == "__main__": seed()python -m src.seed
Beim Start importiert Nexy jedes Modul von src/, um die Aktionen von Teil 6 zu finden. if __name__ == "__main__": behält den Startwert für den obigen Befehl: Die von der App importierte Datei definiert nur seed und berührt nichts.
Zeigen Sie die Notizen aus der Datenbank an
from "@/components/NoteCard.nexy" import NoteCardfrom src.notes import all_notes notes = all_notes()<section class="app-page"> <h1>Notes</h1> {% for note in notes %} <NoteCard id="{{ note.id }}" text="{{ note.text }}" /> {% endfor %}</section>from nexy import notFoundfrom src.notes import find id: prop[int]note = find(id)if note is None: notFound()<section class="app-page"> <h1>{{ note.text }}</h1> <a href="/notes">All notes</a></section>from src.notes import all_notes async def GET(): return [note.model_dump() for note in all_notes()]from "@/components/NoteCard.nexy" import NoteCardfrom src.notes import all_notes notes = all_notes()<section class="app-page"> <h1>Notes</h1> {% for note in notes %} <NoteCard id="{{ note.id }}" text="{{ note.text }}" /> {% endfor %}</section>from nexy import notFoundfrom nexy.decorators import Controllerfrom nexy.hooks import useViewsfrom src.notes import all_notes, find LAYOUT = "src/apps/layout.nexy" def page(view: str, **context: object): return useViews(LAYOUT, context={"children": useViews(view, context=context)}) @Controller("/notes")class NotesController: async def get(self): return page("src/apps/notes/notes_view.nexy") @Controller("/notes/{id}")class NoteController: async def get(self, id: int): note = find(id) if note is None: notFound() return page("src/apps/notes/note_view.nexy", text=note.text) @Controller("/api/notes")class NotesApiController: async def get(self) -> list[dict]: return [note.model_dump() for note in all_notes()]Öffnen Sie http://localhost:3000/notes: die gleichen zwei Karten, jetzt gelesen von dev.db. Starten Sie nexy dev neu und sie sind immer noch da.
Was du gelernt hast
- SQLModel beschreibt eine Tabelle als Klasse; Die Engine in
src/configs/db.pyöffnet die Datenbank. nx migrateschreibt und führt die Migrationen aus:--initeinmal, dann-mund--upgradepro Änderung.- Seiten und Controller rufen einfache Python Funktionen auf, die die Datenbank abfragen: Nexy fügt keine Ebene hinzu.
Nächste Schritte
- Teil 6: Eine Notiz aus einem Formular erstellen
- SQLite: Pragmas, asynchroner Zugriff und Tests auf einer In-Memory-Datenbank