Marking Items Used
Food Tracker (Django)
Chapter 9 · Marking Items Used
A short chapter — and a good place to see how a decision this app's Firebase-based sibling course treated very carefully turns out to need almost no ceremony at all in a relational model.
The View
get_object_or_404 is a small, genuinely useful Django shortcut — it combines the lookup and an automatic 404 response if nothing matches, instead of a manual try/except Item.DoesNotExist.
expiry_date = None: Simply the Right Answer Here
Food Tracker (React + Firebase)'s own Chapter 9 spent real effort explaining why deleteField(), not null, was the correct choice — because in Firestore's schema-on-read model, a document created used-from-the-start never had the field at all, and setting it to null on an existing document would have created a second, inconsistent representation of "no expiry." That entire problem simply doesn't exist here. Chapter 2's expiry_date column is always present, on every row, regardless of history — a used item created directly as used already has expiry_date = NULL from the very start, exactly matching what this view produces on a transition. There's only ever one shape for "no expiry" in a schema-on-write model, because every row already shares the identical set of columns.
deleteField() and None are each the right tool for representing an identical idea ("this item has no expiry date") inside two genuinely different storage models. Schema-on-read has to actively choose between absence and a stored null, and picking wrong creates two different representations. Schema-on-write never has that choice to make at all — every row already has the same columns, so there's exactly one way to represent "no expiry," and it's simply NULL.
Should This Even Be a Plain Link?
mark_used takes no user input — no form fields, nothing to fill in. It's tempting to trigger it from a simple <a href="/mark-used/<id>/"> link. Resist that: a state-changing action behind a plain GET request is a real, substantive correctness problem, not a stylistic nitpick — browser prefetching, crawlers following links, or the browser's own back/forward cache can all trigger a GET request without the user ever intending to. GET is supposed to be safe and non-mutating per HTTP's own semantics; this action should be a POST, even with no actual form fields to collect:
get_object_or_404 everywhere a lookup can failGET — genuinely different from a cosmetic style preference, since it can cause real, unintended state changes with no user action actually behind them.
expiry_date is set to None and saved, the original value is genuinely gone from that row — exactly the same limitation Food Tracker (React + Firebase)'s own Chapter 9 already named for deleteField(). A real undo feature would need to have captured the original date somewhere before clearing it (briefly, in the session, for an "undo" window) or simply ask the user to re-enter it — the same underlying lesson, now confirmed a second time in a completely different storage model.
Where This Course Is Headed
Recipe lookup, Django REST Framework, deployment, and a capstone.
Hands-On Exercises
Explain why expiry_date = None is sufficient here, while the Firebase sibling course specifically needed deleteField() rather than null. What single underlying difference between the two courses' storage models explains this?
📄 View solutionExplain the real problem with triggering mark_used from a plain GET link, giving at least one concrete scenario where this could cause an unintended state change.
📄 View solutionExplain why a future "undo" feature can't simply set expiry_date back to a value after this view has already run, and what a genuine undo implementation would need to do instead.
📄 View solutionChapter 9 Quick Reference
expiry_date = None— sufficient here; schema-on-write guarantees one consistent shape for "no expiry" regardless of historyget_object_or_404— lookup + automatic 404, instead of a manual exception handler- A mutating action must be POST — never a plain GET link, even with no form fields to collect
- Undo still isn't free — the original value is genuinely gone once saved, the same limitation as the Firebase course's own
deleteField() - Next chapter: Recipe Lookup with TheMealDB