Admin CRUD Interface
Website Rebuild with Django
Chapter 10 · Admin CRUD Interface
Food Tracker (Django) 3 already showed that Django's admin can turn a model into a working CRUD interface with a single line of registration, before any custom view exists at all. That's still true here — and, exactly like Website Rebuild with Next.js 10 discovered, it still isn't enough on its own for a genuinely browsable tree. This chapter's real job is the one Chapter 2 deliberately deferred: moving a page that already has children, and doing it correctly.
Free CRUD From One Line
/admin/content/page/ now lists every page, with working add/edit/delete forms — genuinely free, matching Food Tracker (Django) 3's own precedent exactly.
Why the Free Admin Isn't Enough Here
The default admin's own widget for parent is a plain dropdown listing every Page, by its __str__ (Chapter 2's own full_path). For a site with hundreds of pages, that's one enormous, flat, alphabetically-sorted list — no indentation, no sense of depth, no way to browse "show me this page's children." Genuinely unusable once the tree has any real size, for exactly the reason Next.js Rebuild 10 needed its own custom parent picker.
A Cheap Intermediate Step
raw_id_fields replaces the giant dropdown with a search popup instead — a real, worthwhile improvement for almost no code. It's still a flat search, though, not a browsable tree — genuinely better, not genuinely sufficient.
The Real Build: Paying Off Chapter 2's Own Deferred Cost
Chapter 2's own save() method only ever recomputes the one page being saved's full_path — moving a page with children was left as a known gap, deferred specifically until there was a real admin action to attach it to. This is that action.
move_page never manually edits a single full_path string. It doesn't need to — Chapter 2's own save() already recomputes full_path from parent.full_path every time it runs, for any page. The only real trick is processing order: as long as each page is saved only after its own parent's full_path is already correct, calling plain .save() down through the whole subtree — breadth-first, level by level — produces exactly the right cascade, with zero string manipulation anywhere. Chapter 2's own model design didn't just defer this problem; it was quietly built to make the eventual solution this simple.
full_path from a stale parent value — the breadth-first queue above exists specifically to guarantee every page is only processed once its own parent is already correct, one level of the tree at a time.
Deletion Still Respects PROTECT
Chapter 2's own on_delete=PROTECT decision surfaces directly in the admin: attempting to delete a page that still has children raises a clear, real error rather than silently cascading — Django's admin handles a PROTECT violation gracefully out of the box, showing exactly which related objects are blocking the deletion, rather than a raw server error.
Hands-On Exercises
Explain why Django's free automatic admin, even after adding raw_id_fields to the parent field, still isn't sufficient for genuinely browsing or managing this project's own tree structure.
Explain why move_page's own cascade loop processes descendants in parent-first (breadth-first) order rather than in any order, and why it never manually edits a full_path string anywhere.
Explain what happens, per Chapter 2's own on_delete=PROTECT decision, if an admin tries to delete a page that still has children — and why that's the safer default for this specific project.
Chapter 10 Quick Reference
admin.site.register(Page)— free, working CRUD, one line, no custom code- The free admin's real limit — the
parentdropdown is a flat, unstructured list, unusable for browsing a real tree raw_id_fields— a cheap improvement (a search popup instead of a giant select), still not a browsable treemove_page— pays off Chapter 2's own deferred full_path-cascade cost, with zero manual string manipulation- Parent-first, breadth-first order — the only real trick; each page's
save()only produces the right result once its own parent is already correct on_delete=PROTECT, revisited — the admin surfaces a real, clear error rather than a silent cascade or a raw server crash- Next chapter: Deployment