LWAZI // ENGR
← Back to Gallery

Little Lemon

Live
HTML Javascript CSS Python Django
View GitHub Repository →

// System Specification & Design

Little Lemon : Web Application Specification

Type: Full-stack restaurant management web application Purpose: Portfolio deployment (Meta Backend Developer Capstone, cleaned up and hardened for public deployment) Status: Deployed to Render (free tier)


1. Overview

Little Lemon is a Django-based restaurant web application combining server-rendered public pages with a REST API backend for menu management and table reservations. It's a hybrid monolith: Django serves both HTML templates (for browsing/booking) and JSON API endpoints (for programmatic access and the booking page's dynamic JS).


2. Tech Stack

Layer Technology
Backend framework Django 6.0.x
API framework Django REST Framework 3.17.x
Authentication Djoser + DRF Token Authentication
Database SQLite (file-based, ephemeral on host)
Media storage Cloudinary (persistent image hosting)
Static file serving WhiteNoise
WSGI server (production) Gunicorn
Dependency management Pipenv
Frontend Django Templates + vanilla JavaScript (no frontend framework)
Hosting Render (free tier, Python native runtime)
Python version 3.13

3. Data Model

Booking

Field Type Notes
id AutoField Primary key
first_name CharField(255)
reservation_date DateField
reservation_slot IntegerField Hour of day, default 10 (10:00–20:00 range used by frontend)

Constraint: unique_together = ('reservation_slot', 'reservation_date') — prevents double-booking the same time slot on the same day.

Menu

Field Type Notes
id AutoField Primary key
title CharField(255)
price DecimalField(10,2)
inventory IntegerField
image ImageField Optional; stored via Cloudinary in production

4. API Endpoints

Base path: /restaurant/

Endpoint Methods Auth Required Description
/restaurant/api/menu/ GET, POST GET: none · POST: token List menu items / create new item
/restaurant/api/menu/<id>/ GET, PUT, PATCH, DELETE GET: none · writes: token Retrieve/update/delete a single menu item
/restaurant/api/bookings/ GET, POST Token (all methods) List bookings (optionally filtered by ?date=YYYY-MM-DD) / create booking
/restaurant/api/bookings/<id>/ GET, PUT, PATCH, DELETE Token (all methods) Retrieve/update/delete a single booking
/restaurant/api-token-auth/ POST — Exchange username/password for an auth token
/restaurant/health/ GET None Health check — returns {"status": "ok"}

Auth endpoints (via Djoser, mounted at /auth/)

Standard Djoser-generated user management endpoints (registration, current-user detail, token login/logout) — not hand-built, provided by the djoser package.

Page routes (server-rendered HTML)

Route View Template
/restaurant/ home index.html
/restaurant/about/ about about.html
/restaurant/menu/ menu_page menu.html
/restaurant/reservations/ bookings book.html

Note: the bare root / is not currently routed (404s) — the app is namespaced entirely under /restaurant/.


5. Authorization Model

Resource Public (anonymous) Authenticated
Menu — read ✅ Allowed ✅ Allowed
Menu — write (create/update/delete) ❌ Blocked ✅ Allowed
Bookings — read ❌ Blocked ✅ Allowed
Bookings — write ❌ Blocked ✅ Allowed

Implemented via DRF's IsAuthenticatedOrReadOnly (menu) and IsAuthenticated (bookings) permission classes. Authentication is token-based (rest_framework.authentication.TokenAuthentication), obtained via /restaurant/api-token-auth/ or Djoser's endpoints.


6. Frontend Behavior

  • Booking page (book.html): vanilla JS handles the reservation flow client-side — fetches existing bookings for a selected date via the API, renders available/reserved time slots (10:00–20:00, hourly), and submits new bookings via fetch() with CSRF token handling.
  • Menu page (menu.html): server-rendered list of all menu items with image (or placeholder) and price.
  • Static assets: served via WhiteNoise with compressed manifest storage in production.
  • Media assets: menu item images served from Cloudinary CDN.

7. Testing

23 automated tests, organized as:

  • restaurant/tests/test_models.py — model string representations, unique_together constraint enforcement
  • restaurant/tests/test_api.py — Menu API permission boundaries (anonymous read/blocked write, authenticated write), Booking API auth requirements, duplicate-slot rejection, date-filtered queries, serializer output validation
  • restaurant/tests/test_views.py — page rendering for all four template views, health check endpoint

Run via: python manage.py test restaurant


8. Environment Configuration

All sensitive/environment-specific values are externalized via django-environ, loaded from a .env file (local) or host-provided environment variables (production):

Variable Purpose
SECRET_KEY Django cryptographic signing key
DEBUG Debug mode toggle (False in production)
ALLOWED_HOSTS Permitted request Host headers
CLOUDINARY_URL Cloudinary account credentials for media storage
DATABASE_URL Optional — defaults to local SQLite if unset

9. Deployment Architecture

  • Host: Render, free-tier web service
  • Build process: custom build.sh — installs dependencies via Pipenv, runs collectstatic, runs migrate
  • Runtime: Gunicorn (gunicorn Littlelemon.wsgi:application --bind 0.0.0.0:$PORT)
  • Python version pinned via .python-version (3.13) to match Pipfile.lock
  • Database persistence: SQLite is file-based and not persistent across redeploys/restarts on Render's free tier (no attached disk). This is an accepted tradeoff — booking data is demo-only and expected to periodically reset; menu data is intended to be seeded via a data migration so the app never appears empty.
  • Media persistence: solved independently via Cloudinary, which is unaffected by host filesystem resets.
  • CORS: not configured — app is same-origin (API and frontend served from the same Django instance). Would need reconsideration if a separate frontend is ever built against this API.

10. Known Limitations (by design, for this deployment context)

  • No persistent relational database — acceptable for a demo/portfolio deployment, not suitable as-is for a production restaurant system handling real bookings.
  • Free-tier hosting spins down after 15 minutes of inactivity; first request after idle incurs a ~30–60s cold start.
  • No CI/CD pipeline beyond Render's git-push auto-deploy.
  • No rate limiting on public-read endpoints.
  • Bare / route unhandled (404) — app is scoped under /restaurant/.

11. Summary Stats

  • Models: 2 (Booking, Menu)
  • Custom REST endpoints: 5 resource endpoints + 1 health check
  • Auth-provided endpoints: Djoser's standard set (registration, token login/logout, user detail)
  • Automated tests: 23, all passing
  • Third-party services integrated: Cloudinary (media storage)

// Deployment System Status