The Django Admin
Here's the trick that made people fall in love with Django in the first place. You spent Phase 3 defining
two models — Post and Comment — describing your blog's data as Python classes. Django already turned
those into database tables for you. Now it's about to do something that feels almost unfair: it reads those
same model definitions and hands you a complete, working web application for managing that data. A real
back-office, with login, list pages, search boxes, filters, edit forms, delete buttons — and you write
essentially none of it.
📝 The mental model: the admin is a reflection of your models. It doesn't have its own idea of what your data looks like — it asks your models. Each field becomes a form input. Each model becomes a list page. Change the model, and the admin changes with it. That's why the rest of this phase is short: you're not building the admin, you're describing how you want Django to render the one it already built.
This is the single feature that sells Django to teams. "We need an internal tool so the content team can publish posts" is normally a week of CRUD-screen drudgery. In Django it's about four lines of code.
The auto-admin: a back-office for free
Think about what a content manager actually needs to do with a blog: see all the posts, find a specific one, create a new one, fix a typo, unpublish something, delete spam comments. That's the classic CRUD loop — Create, Read, Update, Delete — wrapped in a usable interface.
💡 Writing those screens by hand is the most repetitive work in web development. Django noticed that the information needed to build them — the field names, their types, which fields are required — already lives in your models. So it reads your models (the ones from Phase 3) and builds the whole interface from them automatically.
You get all of this with zero screen-building on your part:
- A list page for each model, showing every row
- Create and edit forms, with the right input type per field (a date picker for
DateField, a dropdown for foreign keys, a checkbox forBooleanField) - Delete with a confirmation step
- Search, filters, and pagination once you ask for them
- A login screen and a permission system, so only trusted staff get in
The admin isn't a toy demo, either — real teams run their entire content operation through it for years.
Enabling it
Good news: the admin is already switched on. When you ran startproject back in Phase 1, Django included
the admin app in INSTALLED_APPS and wired its URLs. The admin lives at /admin/ right now. You just
need two things: a user who's allowed in, and a line telling the admin which models to show.
First, create a superuser — an account with full admin access:
Username: nika
Email address: [email protected]
Password:
Password (again):
Superuser created successfully.
What just happened: Django walked you through creating a privileged account and saved it to the User
table (one of the tables you got from the auth app's migrations). This is the account you'll log in with.
Start the server (python manage.py runserver), visit http://127.0.0.1:8000/admin/, and you can log in —
though the dashboard is nearly empty, because you haven't told the admin about your models yet.
Now open blog/admin.py (Django created this empty file for your app) and register your models:
What just happened: admin.site.register(Post) says "add Post to the admin." Refresh /admin/ and
you'll see a Blog section with Posts and Comments, each clickable. You can now create posts,
edit them, delete them, and manage comments — a full CRUD interface, for those two lines. Nobody wrote a
single form or template.
💡 Remember the
__str__method you added to your models in Phase 3? This is where it pays off. The admin's list page shows each row using__str__. Without it, every post shows up as the uselessPost object (1). With a good__str__returning the post's title, the list is actually readable. That wasn't busywork in Phase 3 — it was setting up for today.
Customizing with ModelAdmin
The default admin is functional but plain — a list of posts shown only by title, with no way to search or
filter. You shape it with a ModelAdmin class: a small configuration object that tells the admin how
to present one model. You attach it with the @admin.register decorator (a tidier replacement for the
admin.site.register call above).
=
=
=
=
=
What just happened: each attribute reshapes the admin for Post:
list_display— which columns show on the list page. Instead of one title column, you now see title, author, published-state, and date side by side. (Each entry is a field name from your model.)list_filter— adds a sidebar of filters. Now a content manager can click "show only published" or filter by date with one click.search_fields— adds a search box at the top that searches across the named fields.ordering— the default sort."-created_at"means newest first (the minus sign reverses it).prepopulated_fields— as you type a post's title, Django auto-fills theslugfield with a URL-friendly version. A small touch that saves real keystrokes. (This assumes yourPosthas aslugfield; drop the line if it doesn't.)
💡 Notice the pattern: you didn't write any UI. You wrote configuration — a handful of tuples naming fields — and Django translated it into search boxes, filter sidebars, and sortable columns. That's the admin's whole philosophy: describe what you want, don't build it.
Inlines: editing related objects together
There's one rough edge so far. Comments belong to posts (that's the foreign key from Phase 3), but in the admin they live on a totally separate page. To read a post and moderate its comments, you'd bounce between two screens. What you actually want is to see — and edit — a post's comments right there on the post's edit page.
That's an inline. You define a small inline class for the related model, then attach it to the parent's
ModelAdmin:
=
= 1
=
=
=
=
=
What just happened: CommentInline tells the admin "Comments are related to Post — render them inside the
Post form." Now when you open a post to edit it, its comments appear as editable rows beneath the post's
fields, and extra = 1 leaves one blank row so you can add a new comment without leaving the page. Django
knew how to connect them because of the ForeignKey from Comment to Post — you just told it where to
display the relationship.
💡
TabularInlinelays the related rows out as a compact table (great for short records like comments). Its siblingStackedInlinestacks each related object as a full form block — better when the related model has many fields and a table would be too cramped. Same idea, two layouts; pick whichever reads better.
What the admin is for (and what it isn't)
The admin feels so powerful that it's tempting to reach for it everywhere. So let's be clear about its job, because misusing it is a genuine security mistake.
💡 The admin is for trusted internal staff — your team, your content editors, your moderators. It's an internal back-office and content-management tool, and at that job it's a massive productivity win. Spin one up and your colleagues can manage data on day one.
⚠️ The admin is not your public, user-facing interface, and it is not a public API. Don't point your blog's readers at
/admin/to write comments, and don't let untrusted users near it. It exposes raw database editing with delete buttons everywhere — in the wrong hands that's a disaster. In production, lock it down: serve it over HTTPS, give staff strong unique passwords, and consider moving it off the obvious/admin/URL or restricting it by IP. Treat it like the keys to the database, because that's roughly what it is.
What your public visitors see — the actual blog pages with their own design — is a separate thing you build deliberately. That's the next two phases: templates render the public pages (Phase 5), and forms let visitors safely submit comments (Phase 6).
💡 Step back and notice what just happened across Phases 3 and 4. You defined
PostandCommentonce, as Python classes. From that single definition Django gave you the database schema (Phase 3), and now a complete admin interface (Phase 4) — and next it'll generate forms for you too (Phase 6). Define the model once, get the rest for free. That "don't repeat yourself" payoff is the heart of why Django feels the way it does.
Recap
- Django generates a full CRUD back-office — list, search, filter, create, edit, delete — automatically from your models. It's the framework's signature feature.
- The admin is already enabled. Create access with
python manage.py createsuperuser, then register each model withadmin.site.register(Model)(or the@admin.registerdecorator) inadmin.py. - A model's
__str__controls how its rows appear in the admin — which is why you wrote good__str__methods in Phase 3. - A
ModelAdminclass customizes one model's admin vialist_display,list_filter,search_fields,ordering, andprepopulated_fields— configuration, not UI code. - Inlines (
TabularInline/StackedInline) let you edit related objects, like a post's comments, on the parent's edit page. - The admin is for trusted staff only — an internal tool, never a public UI or API. Lock it down in production.
Quick check
[
{
"q": "Where does the Django admin get the information it needs to build its forms and list pages?",
"choices": ["From a separate config file you write by hand", "From your model definitions", "From the database's raw column metadata at runtime"],
"answer": 1,
"explain": "The admin reflects your models — each field becomes a form input and each model becomes a list page. Change the model and the admin changes with it."
},
{
"q": "What does a ModelAdmin's `list_display` attribute control?",
"choices": ["Which fields are required when creating a record", "Which columns appear on the model's list page in the admin", "Which users are allowed to see the model"],
"answer": 1,
"explain": "`list_display` is a tuple of field names shown as columns on the list page, so you see more than just the `__str__` value."
},
{
"q": "Which statement about the Django admin is correct?",
"choices": ["It's meant to be your public, user-facing interface", "It's an internal back-office for trusted staff and should be locked down in production", "It replaces the need to ever write templates or forms"],
"answer": 1,
"explain": "The admin is a powerful internal tool for trusted staff. It is not a public UI or API — expose it carefully, and build public pages with templates and forms instead."
}
]
← Phase 3: Models & the ORM · Guide overview · Phase 5: Templates & the MTV Pattern →
Check your understanding
1. Where does the Django admin get the information it needs to build its forms and list pages?
2. What does a ModelAdmin's `list_display` attribute control?
3. Which statement about the Django admin is correct?