Elk bericht bewaren: een eigen sleutel
Hier bouw je op verder: Lijsten
Weet je dit niet meer zeker? Lees het terug voordat je verdergaat.
- Python Lijsten
In Een formulier opslaan overschreef elke naam de vorige, want ze gingen allemaal onder de sleutel laatste_naam. Voor een gastenboek wil je elk bericht bewaren. Dan heeft elk bericht een eigen sleutel nodig.
Een nieuwe sleutel per bericht
time.time_ns() geeft het aantal nanoseconden sinds 1970. Dat getal is bij elk bericht anders, dus elk bericht krijgt een nieuwe sleutel. Dit gastenboek bouw je in de volgende lessen verder uit, dus maak het echt.
Formulier (static/pages/gastenboek_form.html)
<!DOCTYPE html>
<html>
<body>
<h1>Gastenboek</h1>
<form method="post" action="/gastenboek">
<input type="text" name="naam" placeholder="Je naam" required>
<input type="text" name="bericht" placeholder="Je bericht" required>
<button type="submit">Verstuur</button>
</form>
</body>
</html>
Endpoints (main.py, onder de endpoints van de naam)
Zet import time bovenaan je main.py, bij de andere imports.
@app.get("/gastenboek")
async def gastenboek_form():
return FileResponse("static/pages/gastenboek_form.html")
@app.post("/gastenboek")
async def gastenboek_opslaan(naam: str = Form(...), bericht: str = Form(...)):
with SqliteDict("gastenboek.db") as db:
sleutel = f"bericht_{time.time_ns()}"
db[sleutel] = {"naam": naam, "bericht": bericht}
db.commit()
return {"bericht": "Bedankt voor je bericht"}
@app.get("/berichten")
async def berichten():
with SqliteDict("gastenboek.db") as db:
alle_berichten = list(db.values())
return {"berichten": alle_berichten}
- Regel 8:
Zo krijgt elk bericht een nieuwe sleutel, zoals
bericht_1767225600123456789, en overschrijven twee berichten elkaar niet meer. - Regel 9:
De waarde is een dictionary met beide velden, zoals in Een dictionary als waarde.
- Regel 16:
db.values()geeft alle waarden, zonder sleutels, maar haalt ze pas op als iets erdoorheen loopt.list()doet dat meteen, binnen hetwith-blok. Zonderlist()loopt FastAPI er pas doorheen als hij de JSON maakt, en dan is de database al dicht: je krijgtAttributeError: 'NoneType' object has no attribute 'select'.
Gebruik time.time_ns() (nanoseconden) en niet int(time.time()) (hele seconden). Twee berichten die binnen dezelfde seconde binnenkomen, krijgen anders dezelfde sleutel, en dan overschrijft het tweede het eerste.
Het gastenboek testen
- Ga naar
http://127.0.0.1:8000/gastenboeken verstuur twee berichten. - Ga naar
http://127.0.0.1:8000/berichten. Je ziet ze allebei, als JSON. - Stop de server, start hem opnieuw en kijk nog een keer: ze staan er nog.
Blijft je data niet bewaard? Kijk dan of je db.commit() niet bent vergeten. Meer oplossingen staan op Er gaat iets mis.
Zo ziet je main.py er nu uit
Dit is je hele main.py na deze les, zonder wat je in de opdrachten maakte.
import random
import time
from fastapi import FastAPI, Form, Request
from fastapi.responses import FileResponse, HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from sqlitedict import SqliteDict
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
@app.get("/")
async def home():
return FileResponse("static/pages/home.html")
@app.get("/info")
async def info():
return {"naam": "Jouw Naam", "leeftijd": 16}
@app.get("/pagina", response_class=HTMLResponse)
async def pagina():
return """
<!DOCTYPE html>
<html>
<head>
<title>Mijn Pagina</title>
</head>
<body>
<h1>Hallo!</h1>
<p>Dit is mijn eerste HTML pagina.</p>
</body>
</html>
"""
@app.get("/styled", response_class=HTMLResponse)
async def styled():
return """
<!DOCTYPE html>
<html>
<head>
<title>Met CSS</title>
<style>
h1 { color: blue; }
p { color: gray; }
</style>
</head>
<body>
<h1>Gestylede pagina</h1>
<p>Deze tekst is grijs.</p>
</body>
</html>
"""
@app.get("/about")
async def about():
return FileResponse("static/pages/about.html")
DIEREN = {
"kat": "Een kat spint als hij tevreden is.",
"hond": "Een hond kwispelt als hij blij is.",
"vis": "Een vis ademt met kieuwen.",
}
@app.get("/dier/{naam}", response_class=HTMLResponse)
async def dier(naam: str):
tekst = DIEREN.get(naam, "Dit dier staat nog niet op de site.")
return f"""
<!DOCTYPE html>
<html>
<body>
<h1>Over dieren</h1>
<p>{tekst}</p>
<a href="/">Terug naar Home</a>
</body>
</html>
"""
@app.get("/zoek")
async def zoek(term: str):
return {"je_zocht": term}
@app.get("/foto")
async def foto():
return FileResponse("static/pages/foto.html")
@app.get("/dobbelsteen")
async def dobbelsteen(request: Request):
worp = random.randint(1, 6)
return templates.TemplateResponse(
request,
"dobbelsteen.html",
{"worp": worp}
)
@app.get("/naam")
async def toon_formulier():
return FileResponse("static/pages/naam_form.html")
@app.post("/verstuur")
async def ontvang_naam(naam: str = Form(...)):
print(f"Naam ontvangen: {naam}")
return {"bericht": f"Hallo {naam}!"}
@app.get("/groet")
async def groet_form():
return FileResponse("static/pages/groet_form.html")
@app.post("/groet")
async def groet_verwerk(request: Request, naam: str = Form(...)):
return templates.TemplateResponse(
request,
"groet_resultaat.html",
{"naam": naam}
)
@app.get("/opslaan")
async def naam_formulier():
return FileResponse("static/pages/naam_opslaan.html")
@app.post("/opslaan")
async def naam_opslaan(naam: str = Form(...)):
with SqliteDict("namen.db") as db:
db["laatste_naam"] = naam
db.commit()
return {"bericht": f"'{naam}' opgeslagen!"}
@app.get("/toon_naam")
async def toon_naam():
with SqliteDict("namen.db") as db:
naam = db.get("laatste_naam", "Nog niets opgeslagen")
return {"naam": naam}
@app.get("/gastenboek")
async def gastenboek_form():
return FileResponse("static/pages/gastenboek_form.html")
@app.post("/gastenboek")
async def gastenboek_opslaan(naam: str = Form(...), bericht: str = Form(...)):
with SqliteDict("gastenboek.db") as db:
sleutel = f"bericht_{time.time_ns()}"
db[sleutel] = {"naam": naam, "bericht": bericht}
db.commit()
return {"bericht": "Bedankt voor je bericht"}
@app.get("/berichten")
async def berichten():
with SqliteDict("gastenboek.db") as db:
alle_berichten = list(db.values())
return {"berichten": alle_berichten}
Opdrachten
Opdracht 1: Predict - Hoeveel berichten?
Iemand haalt time.time_ns() weg en schrijft sleutel = "bericht". Daarna verstuurt iemand drie berichten.
Opdracht 2: Run
Maak het gastenboek uit deze les. Test of de berichten bewaard blijven door de server te stoppen en opnieuw te starten.
Opdracht 3: Investigate - Wat staat er echt in?
Laat /berichten ook de sleutels zien: vervang list(db.values()) door list(db.items()). Wat verandert er in de JSON?
Antwoord
Elk bericht wordt een paar: eerst de sleutel, dan de dictionary, zoals ["bericht_1767225600123456789", {"naam": "Sam", "bericht": "Hoi"}]. Aan de getallen zie je in welke volgorde de berichten binnenkwamen. De sleutel heb je later nodig om één bericht aan te wijzen.
Opdracht 4: Make - Hoeveel berichten
Maak een endpoint /aantal dat teruggeeft hoeveel berichten er in het gastenboek staan: {"aantal":3}.
Tip
len(db) geeft het aantal sleutels in de database, net als bij een dictionary.
Antwoord
@app.get("/aantal")
async def aantal():
with SqliteDict("gastenboek.db") as db:
hoeveel = len(db)
return {"aantal": hoeveel}
Na het versturen sta je nu op een pagina met alleen {"bericht":"Bedankt voor je bericht"}. In Doorsturen na opslaan (redirect) kom je daarna vanzelf bij de berichten uit.