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.

Step 1 — Seeding the First Test Data via the Admin

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.

Step 2 — Scanning a Real Item

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.

Step 3 — What's Actually in the Row

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.

Step 4 — The Dashboard Flags It

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.

Step 5 — Searching Her History

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.

Step 6 — Marking It Used

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.

Step 7 — A Recipe Suggestion

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.

Step 8 — Faster Than It Used to Be

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.

Step 9 — All of This, in Production

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

StepChapter(s) applied
1 — Admin seedingChapter 1 (batteries included), Chapter 3 (admin registration)
2 — Scanning an itemChapter 5 (scanning), Chapter 4 (lookup + cache), Chapter 6 (add-item form, commit=False)
3 — The stored rowChapter 2 (model design, auto_now_add)
4 — Expiry alertChapter 7 (queryset, timezone.now())
5 — SearchChapter 8 (icontains, debounced JS)
6 — Marking usedChapter 9 (expiry_date=None, POST-only action)
7 — Recipe suggestionChapter 10 (fan-out, relevance sort, JSONField cache)
8 — Faster interactionsChapter 11 (DRF, resolving Chapter 5's own limitation)
9 — Real productionChapter 12 (deployment)
What this whole course was really about
Chapter 1 opened by contrasting Django's batteries-included philosophy against FastAPI's thin-API-layer approach. Every step above is that same claim made concrete: an admin panel free, a form system reading directly from the same model, an ORM with native substring search and no composite-index requirement, and DRF as the exact same philosophy escalating one level further once genuinely justified. Where Food Tracker (FastAPI) would hand-write a serializer schema, where Food Tracker (React + Firebase) would need a shadow field for search, this course's own answer was already sitting inside the framework — not because Django is simply "better," but because it made a real, different bet about how much to decide up front.
Honest scope note
This capstone deliberately stops short of several things: the weekly meal planner named as future work all the way back in Chapter 1 was never built; there's no offline/PWA support; no multi-user ownership model was ever built into this course at all — 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

Exercise 1

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 solution
Exercise 2

Pick 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 solution
Exercise 3

Explain 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 solution

Chapter 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.auth being available, no fix for Chapter 10's own sequential recipe lookup, no automated tests/CI
  • Food Tracker (Django) is now complete — 13/13 chapters