Django from Basics to Production · Capstone
Capstone: An Event Ticketing App in Django
Design a Django and DRF app for selling event tickets: models, API, concurrency, permissions, tests and deployment.
Scenario
A small events company sells tickets for 20 to 50 events a month. Organisers create events and ticket types with limited quantities; customers buy tickets on the website and in a mobile app. Popular events sell out in minutes.
Requirements
- Never sell more tickets than available, even when hundreds of people buy at once.
- Organisers manage only their own events; customers see only their own orders.
- A confirmation email after each successful purchase.
- An admin view for support staff.
Constraints
- Django 6.1, DRF, PostgreSQL, Redis available.
- Deployed as containers behind a load balancer with TLS.
Deliverables
- Models and relationships.
- API design with serializers, viewsets and permissions.
- Concurrency for ticket sales.
- Testing strategy.
- Deployment settings and process.
Hints
- The database is the place to enforce limits.
- Emails must not be sent for purchases that didn't happen.
Draft each deliverable below before revealing the model answer and rubric.
Further reading: Django documentation · Django REST framework
Deliverables
Describe the models.
Model answer
Event(organiser FK User, title, starts_at, venue),TicketType(event FK, name, price Decimal, quantity, sold)with a databaseCheckConstraint(sold <= quantity).Order(customer FK User, created_at, status)andOrderItem(order FK, ticket_type FK, quantity).- Indexes on
Event.starts_atand foreign keys used in filters;PROTECTon ticket types with sold tickets.
Rubric
Look for a sold counter protected by a database constraint, Decimal prices and sensible deletion rules.
Describe the API.
Model answer
- Public read-only
EventViewSet(list with pagination, filters by date); organiserManageEventViewSetwithget_querysetfiltered toorganiser=request.userand owner set inperform_create. OrderViewSetfor customers: create and list own orders only.- Serializers with explicit fields, read-only status and totals, validation of quantities (1 to 10 per order).
Rubric
Strong answers scope querysets per user, set owners server-side and keep validation in serializers.
How do you prevent overselling?
Model answer
- In
transaction.atomic(): create the order, thenTicketType.objects.filter(pk=..., sold__lte=F("quantity") - qty).update(sold=F("sold") + qty); zero rows updated means sold out, so raise and roll back. - The check constraint is a second line of defence.
- Confirmation emails queued with
transaction.on_committo a Celery or RQ worker.
Rubric
Look for an atomic conditional update or row locking inside a transaction, plus on_commit for side effects.
Describe testing.
Model answer
- TestCase with setUpTestData for events and users; API tests for permissions (organiser A can't edit B's event, customers can't see others' orders).
- A concurrency test with TransactionTestCase and threads (against PostgreSQL) buying the last tickets: exactly the available number succeed.
- assertNumQueries on event lists and order history;
check --deployin CI.
Rubric
Strong answers include permission tests, a real concurrency test on PostgreSQL and query-count assertions.
Describe deployment.
Model answer
- Settings from the environment: DEBUG off, SECRET_KEY, ALLOWED_HOSTS, DATABASE_URL; HTTPS settings with SECURE_PROXY_SSL_HEADER for the load balancer.
- Gunicorn (or uvicorn for ASGI) in a non-root container; static files via collectstatic and WhiteNoise or a CDN.
- Migrations run as a release job before new containers receive traffic; health endpoint; JSON logging; the Django admin restricted to staff over HTTPS.
Rubric
Look for environment-driven settings, a production server, static file handling, migrations as a release step and secured admin.
Open in the app to mark lessons complete, take notes and track your progress.