Neue Routen in main.py:
python
@app.post('/kunden/{kunden_id}/loeschen')
def kunde_loeschen(kunden_id: int):
with pool.connection() as conn:
conn.execute('DELETE FROM kunden WHERE id = %s', (kunden_id,))
return RedirectResponse('/', status_code=303)
@app.get('/api/umsatz-pro-stadt')
def umsatz_pro_stadt():
with pool.connection() as conn:
return conn.execute('''
SELECT COALESCE(stadt, 'unbekannt') AS stadt, SUM(umsatz) AS umsatz
FROM kunden
GROUP BY stadt
ORDER BY umsatz DESC
''').fetchall()
In kundenliste die Summe berechnen und ans Template geben:
python
gesamt = sum(k['umsatz'] for k in kunden)
return templates.TemplateResponse(
request, 'kunden.html', {'kunden': kunden, 'q': q, 'gesamt': gesamt}
)
In kunden.html die Löschen-Spalte und die Summenzeile:
html
<td>
<form method="post" action="/kunden/{{ k.id }}/loeschen">
<button type="submit">Löschen</button>
</form>
</td>
html
<tfoot>
<tr><th colspan="3">Summe</th><th>{{ '%.2f'|format(gesamt) }}</th></tr>
</tfoot>
(Die Kopfzeile bekommt eine fünfte leere Spalte, und in der Summenzeile wird colspan entsprechend angepasst.)
Was passiert:
- Browser-Formulare können nur
GETundPOST. Deshalb löschst du über ein kleines Formular mitPOST, nicht mitDELETE. {kunden_id}im Pfad wird alsinterkannt und kommt fertig umgewandelt in der Funktion an.COALESCE(stadt, 'unbekannt')ersetztNULLim SQL durch einen Text.GROUP BYundSUMübernimmt die Datenbank.- Die Summe in Python (
sum(...)) nutzt die bereits geladenen Zeilen: Die Summe passt damit zur Suche, die gerade angezeigt wird.
Warum so: Die Trennung in drei Schichten macht die Anwendung leicht änderbar: Routen (was passiert bei einer Anfrage), Datenbank (woher kommen die Daten) und Templates (wie sieht es aus). Dieselben Daten kannst du als HTML-Seite (für Menschen) und als JSON (für andere Programme) anbieten.