Django POST/Redirect/GET
A successful POST should usually redirect to a GET page. This separates the mutation from its displayed result and reduces accidental resubmission on refresh.
How it works
The booking endpoint validates seats, calls the reservation service and redirects only after acceptance. Invalid input renders errors without creating a record; a conflict uses an explicit 409. The redirect pattern does not guarantee idempotency or prevent duplicate requests by itself. The database’s unique user/workshop constraint provides a separate protection for this lab’s one-reservation rule.
Run in the supplied local Django project
This lesson uses Django 5.2.18 LTS with Python 3.12. Download the source downloader and run node download-project.mjs http://127.0.0.1:8000 with Node 22 while previewing locally. It creates a new bhylu-django-lab folder and refuses to overwrite an existing one. After deployment, the default base is bhylu.com; until then use your local preview URL.
Inside that new folder, create a virtual environment, install requirements.txt and run the complete lesson file shown below. Every lesson creates a fresh in-memory SQLite database and temporary upload storage. It does not edit the interactive lab’s db.sqlite3 or Bhylu’s database. The visible listing below is the lesson’s core code; its complete downloaded file includes the shared bootstrap.
Django does not run in Bhylu’s current browser playground. No Apache, MySQL or global PHP configuration change is needed. See the setup lesson for the full project and commands.
Example
Run .venv\Scripts\python.exe lessons\post-redirect.py from the downloaded project root. Download the complete lesson file; keep it under lessons/. The project’s shared models, forms, views and fixture are prerequisites, not a hidden online runtime.
from django.contrib.auth import get_user_model
client = Client()
client.force_login(get_user_model().objects.get(username="alex"))
response = client.post("/workshops/lamp-care/book/", {"seats":"2"})
print(response.status_code, response.headers["Location"])
print(Reservation.objects.get().seats)
print(Workshop.objects.get(slug="lamp-care").remaining)
Expected result
302 /workshops/lamp-care/ 2 5
Two seats are recorded and the remaining count falls from seven to five. The test client intentionally does not enforce CSRF by default; the next lesson tests that separate security boundary with enforcement enabled.
Use Django’s versioned official documentation for exact API contracts and the deployment checklist before a separate release.