Capstone: A Complete, Working Food Tracker
Food Tracker (Django)
Chapter 13 · Capstone: A Complete, Working Food Tracker
Aisha manages this deployment day to day. Every step below is a real, working feature, each one built in a specific earlier chapter.
Before a single real user touches the app, Aisha logs into /admin/ and adds a couple of test items by hand — Chapter 1's own promise, delivered concretely by Chapter 3's two-line admin.site.register(Item), exercised here exactly as intended: real CRUD, before any custom view existed.
A user scans a yogurt carton's barcode. Chapter 5's scan.js decodes it and navigates to Chapter 4's lookup view, which checks BarcodeCache, finds nothing, queries Open Food Facts, and caches the result. The confirm-and-save form (Chapter 6) pre-fills name and category; commit=False sets status="active" before the actual save.
The real database row reflects every decision Chapter 2 made: name, barcode, category="dairy", status="active", a real expiry_date, and added_at set automatically by auto_now_add — never trusting a client-supplied value for that field.
A few days later, Chapter 7's dashboard query — status="active", expiry_date__lte a timezone.now()-based threshold — surfaces the item as expiring soon, with no composite-index drama at all, exactly the fair SQL advantage that chapter named.
Wanting to buy more of something bought before, a user types "yog". Chapter 8's debounced JS hits /search/, which uses name__icontains — genuine, native, case-insensitive substring matching, no shadow field ever needed — finding "Greek Yogurt" instantly.
The yogurt gets finished. A POST-only "Mark Used" button — never a plain GET link, per Chapter 9's own correctness point — sets status="used", stamps used_at, and sets expiry_date = None. The item stays in history forever, exactly as Chapter 2 designed, just without an expiry date attached anymore.
With chicken and eggs both nearing expiry, Chapter 10's suggest_recipes view fans out one request per ingredient to TheMealDB — sequentially, with the honest performance cost that chapter named — merges the results, and sorts by matched_ingredients count, surfacing a recipe using both chicken and eggs above one using only either alone.
By now, the team has followed Chapter 11's own hybrid path: the admin and dashboard stayed exactly as they were, but search, add-item, mark-used, and recipe lookup moved behind real DRF ViewSets, called via fetch() with the CSRF token included correctly. Chapter 5's own full-page-reload limitation — named honestly back when it was first built — is genuinely resolved here, not by rewriting that chapter's logic, but by Chapter 11 giving the app somewhere better to send its requests.
The whole session happens on a real deployment: DEBUG=False with ALLOWED_HOSTS set correctly, SECRET_KEY loaded from the environment, static files collected and served, gunicorn handling real traffic — SQLite still genuinely appropriate at this app's own realistic scale, exactly as Chapter 12 concluded.
Chapter Attribution
| Step | Chapter(s) applied |
|---|---|
| 1 — Admin seeding | Chapter 1 (batteries included), Chapter 3 (admin registration) |
| 2 — Scanning an item | Chapter 5 (scanning), Chapter 4 (lookup + cache), Chapter 6 (add-item form, commit=False) |
| 3 — The stored row | Chapter 2 (model design, auto_now_add) |
| 4 — Expiry alert | Chapter 7 (queryset, timezone.now()) |
| 5 — Search | Chapter 8 (icontains, debounced JS) |
| 6 — Marking used | Chapter 9 (expiry_date=None, POST-only action) |
| 7 — Recipe suggestion | Chapter 10 (fan-out, relevance sort, JSONField cache) |
| 8 — Faster interactions | Chapter 11 (DRF, resolving Chapter 5's own limitation) |
| 9 — Real production | Chapter 12 (deployment) |
django.contrib.auth is genuinely available and easy to add, but this course never actually wired it in, so every Item row remains ownerless, unlike the Firebase sibling course's own Chapter 11; Chapter 10's own sequential recipe-lookup performance cost was named honestly but never actually fixed; and no automated test suite or CI pipeline was covered anywhere in this course. Each is a genuine, reasonable next step — none were quietly assumed to already be done.
Hands-On Exercises
Explain how Step 8 resolves Chapter 5's own full-page-reload limitation without rewriting that chapter's own scanning logic. What specifically changed to make this possible?
📄 View solutionPick any two steps from Aisha's session and explain how each one depends on at least two earlier chapters working together, not just one chapter in isolation.
📄 View solutionExplain why the honest scope note specifically calls out the lack of a multi-user ownership model as a genuine gap, rather than assuming Django's built-in django.contrib.auth makes this a non-issue.
📄 View solutionChapter 13 Quick Reference — Course Complete
- 9 steps, 12 prior chapters — one continuous, realistic session with the finished, deployed app
- This course's own throughline, closed out: batteries included, one integrated framework deciding more up front than FastAPI's thin-layer approach
- Honest scope note: no meal planner, no offline/PWA, no multi-user ownership model despite
django.contrib.authbeing available, no fix for Chapter 10's own sequential recipe lookup, no automated tests/CI - Food Tracker (Django) is now complete — 13/13 chapters