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
51 lines
1.6 KiB
Python
51 lines
1.6 KiB
Python
"""Stammdaten vorbefüllen: Kategorien und Grössen.
|
|
|
|
Läuft beim Start und legt nur an, was fehlt - so gehen von Hand ergänzte
|
|
Einträge bei einem Neustart nicht verloren.
|
|
"""
|
|
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.models import Groesse, Kategorie
|
|
|
|
KATEGORIEN = [
|
|
("Hosen", "hosen"),
|
|
("Oberteile", "oberteile"),
|
|
("Pullover", "pullover"),
|
|
("Jacken", "jacken"),
|
|
("Kleider & Röcke", "kleider-roecke"),
|
|
("Schuhe", "schuhe"),
|
|
("Unterwäsche & Socken", "unterwaesche-socken"),
|
|
("Schlafanzüge", "schlafanzuege"),
|
|
("Mützen & Handschuhe", "muetzen-handschuhe"),
|
|
("Sonstiges", "sonstiges"),
|
|
]
|
|
|
|
# Reihenfolge ausdrücklich vergeben. Als Text sortiert stünde "104" vor
|
|
# "56" - genau der Grund für die eigene Tabelle.
|
|
GROESSEN = [
|
|
# Babys und Kleinkinder (Körpergrösse in cm)
|
|
*[(str(cm), i) for i, cm in enumerate(range(50, 105, 6), start=10)],
|
|
# Kinder
|
|
*[(f"{cm}/{cm + 6}", 100 + i) for i, cm in enumerate(range(98, 170, 12))],
|
|
*[(str(cm), 200 + i) for i, cm in enumerate(range(104, 182, 6))],
|
|
# Schuhe
|
|
*[(f"Schuh {nr}", 300 + i) for i, nr in enumerate(range(16, 41))],
|
|
]
|
|
|
|
|
|
def stammdaten_anlegen(db: Session) -> None:
|
|
vorhanden = set(db.scalars(select(Kategorie.slug)))
|
|
for name, slug in KATEGORIEN:
|
|
if slug not in vorhanden:
|
|
db.add(Kategorie(name=name, slug=slug))
|
|
|
|
bekannt = set(db.scalars(select(Groesse.label)))
|
|
for label, sort_order in GROESSEN:
|
|
if label not in bekannt:
|
|
db.add(Groesse(label=label, sort_order=sort_order))
|
|
bekannt.add(label)
|
|
|
|
db.commit()
|