Admin CRUD Interface
Website Rebuild with Ruby on Rails
Chapter 10 · Admin CRUD Interface
Django's admin is a live, framework-owned interface that reads the model at runtime — always current, never a file you'd open and edit. Laravel has nothing free by default at all. Rails reaches a genuine third position: rails generate scaffold produces real, ordinary source files — a model, a migration, a Controller with all seven RESTful actions, ERB views — in one command, at generation time. It's "free" the way a starting template is free, not the way a live, self-updating admin panel is free.
What Scaffold Would Generate
For a plain, flat resource like this hypothetical AdminNote, that one command would generate a working index/show/new/edit/create/update/destroy interface immediately — genuinely useful, genuinely "free" in the one-time sense. But it's not what builds this chapter's own admin interface.
Page itself isn't scaffoldedPage's own self-referencing tree — a parent picker, reparenting logic, a delete guard tied to children — has no equivalent in what scaffold produces by default. This is the same reason Django's and Laravel's own admin interfaces both needed hand-written code too: a generic starting point, whether live-introspected or one-time-generated, still runs out exactly where this specific model's own real complexity begins.
The Parent Picker: Same Searchable List
Matching every sibling course's own approach — a plain, client-side-filterable flat list of every other page's full_path, rather than a nested tree widget, for the same scale-justified reason established since the Next.js rebuild's own Chapter 10.
Paying Off Chapter 2's Deferred Cost
Rails' own version of the same explicit recursive walk both Laravel's updateDescendantPaths() and this course's own three predecessors ultimately needed — move_to updates the moved page first, then recurses through update_descendant_paths! to fix every descendant's own stale full_path.
Delete Refusal — a Callback to Chapter 2's Own Finding
dependent: :restrict_with_error as an application-level check — a real limitation, since a raw SQL delete could bypass it in a way Laravel's database-level restrictOnDelete() never could. Here in Chapter 10, that same application-level design pays a genuine ergonomic dividend: destroy simply returns false and populates page.errors when a child still exists — no begin/rescue block needed at all. Laravel's Chapter 10 needed a try/catch around a raw QueryException specifically because its own protection lived one layer deeper, at the database. The same design choice that was a real weakness in Chapter 2 is a real convenience here — worth naming both sides honestly rather than declaring one approach simply better.
Three Admin Interfaces, Compared
| Django | Laravel | Rails | |
|---|---|---|---|
| Free admin? | Yes — live, runtime-introspected | No — Nova/Filament are separate packages | Yes, but one-time — rails generate scaffold writes real files |
| Reparent cascade | Implicit — re-triggered save() | Explicit — updateDescendantPaths() | Explicit — update_descendant_paths! |
| Delete-with-children handling | Refused, surfaced in the admin | Caught QueryException, surfaced manually | Returns false, errors already populated — no exception handling needed |
Hands-On Exercises
Explain what rails generate scaffold produces, and explain why calling it a "one-time free" is a meaningful distinction from Django's own live, runtime-introspected admin.
Explain why Page's own admin interface isn't built by running rails generate scaffold directly against it.
Explain the two-sided finding about dependent: :restrict_with_error — a real limitation back in Chapter 2, but a genuine advantage here — and explain specifically why no begin/rescue block is needed in this chapter's own destroy action.
Chapter 10 Quick Reference
rails generate scaffold— a genuine third kind of "free," real editable code, generated once- Not run against
Pageitself — its tree logic needs hand-written code, matching both siblings' own reasons move_to/update_descendant_paths!— the explicit recursive cascade paying off Chapter 2's deferred cost- Two-sided finding — Chapter 2's application-level delete guard was a real limitation there, a real ergonomic win here
- Next chapter: Deployment