Phase 1: Datenmodell, CRUD-API und Stammdaten
FastAPI mit SQLAlchemy und SQLite, Migrationen über Alembic. Die
Endpunkte aus projekt.md für items, categories und sizes stehen samt
Reservierung; Bilder und Anmeldung folgen in Phase 2 und 3.
Umgesetzt wie in Plan.md festgelegt:
- Feste Wertelisten als Enum UND als CHECK in der Datenbank. Die
CHECK-Bedingung ist der eigentliche Schutz: an SQLAlchemy vorbei (Import,
sqlite3 von Hand) käme sonst "Gril" durch, und die Filter griffen still
nicht mehr.
- Status "draft" für den Stapel-Import. Entwürfe erscheinen weder in der
Galerie noch in GET /items, solange nicht ausdrücklich status=draft
angefragt wird, und lassen sich nicht reservieren.
- Titel ist freiwillig und wird sonst beim ANZEIGEN aus Kategorie und
Grösse gebildet ("Jacken 98/104") - nicht beim Speichern, damit er einer
späteren Korrektur der Grösse folgt.
- reservation_token: nur wer es hat, kann die eigene Reservierung aufheben.
Verglichen mit compare_digest, gelöscht beim Freigeben und beim
Erledigen, damit ein alter Link nicht später eine fremde Reservierung
aufhebt.
- Eigene sizes-Tabelle mit sort_order statt SELECT DISTINCT: sonst stünde
"104" vor "56" und jeder Tippfehler würde zur Filteroption.
- Pagination auf GET /items, in projekt.md nicht vorgesehen.
Zwei SQLite-Eigenheiten, die leicht untergehen: foreign_keys ist
standardmässig AUS (ohne PRAGMA greift ON DELETE CASCADE nicht), und
check_same_thread muss für FastAPI abgeschaltet werden. Beides in
database.py, dazu WAL fürs gleichzeitige Lesen.
24 Tests, alle grün. Zusätzlich von Hand geprüft: Migration anwenden,
Eintrag anlegen, reservieren, ohne Token freigeben (403), mit Token
freigeben (200), Swagger UI erreichbar.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GR4bNaj9GtRu57J4Niii8o
This commit is contained in:
+45
@@ -0,0 +1,45 @@
|
||||
"""Einstiegspunkt der Anwendung.
|
||||
|
||||
FastAPI liefert später sowohl die REST-API als auch die gerenderten Seiten
|
||||
aus - ein Dienst, eine Datenbank, kein getrenntes Frontend (Variante A aus
|
||||
Plan.md).
|
||||
"""
|
||||
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
from app.config import einstellungen
|
||||
from app.database import SessionLocal, engine
|
||||
from app.routers import categories, items
|
||||
from app.seed import stammdaten_anlegen
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lebenszyklus(_app: FastAPI):
|
||||
# Die Tabellen legt Alembic an (alembic upgrade head). Hier werden nur
|
||||
# die Stammdaten ergänzt, damit eine frische Datenbank sofort brauchbar
|
||||
# ist - Kategorien und Grössen sind Voraussetzung fürs Erfassen.
|
||||
cfg = einstellungen()
|
||||
cfg.upload_pfad.mkdir(parents=True, exist_ok=True)
|
||||
with SessionLocal() as db:
|
||||
stammdaten_anlegen(db)
|
||||
yield
|
||||
engine.dispose()
|
||||
|
||||
|
||||
app = FastAPI(
|
||||
title="Kinderkleider-Börse",
|
||||
description="Zu klein gewordene Kinderkleidung katalogisieren und weitergeben.",
|
||||
version="0.1.0",
|
||||
lifespan=lebenszyklus,
|
||||
)
|
||||
|
||||
app.include_router(categories.router)
|
||||
app.include_router(items.router)
|
||||
|
||||
|
||||
@app.get("/health", tags=["Betrieb"])
|
||||
def health():
|
||||
"""Wird vom Docker-Healthcheck und von der CI benutzt."""
|
||||
return {"status": "ok"}
|
||||
Reference in New Issue
Block a user