The Django admin as an operations tool
The Django admin is the framework's most famous feature, and for good reason: register your models and you get a complete, secure, database-backed web interface — for free — where non-technical staff can manage real data. For an internal tool like Nidaan's back office, the admin can be most of the product. This lesson shows how to turn the default admin into a genuinely useful operations tool, and where its limits are.
The admin exists the moment you register a model
The admin app is enabled by default (it is in INSTALLED_APPS). To manage a model, register it:
# patients/admin.py
from django.contrib import admin
from .models import Doctor, Patient, Appointment
admin.site.register(Doctor)
Create a superuser and log in:
python manage.py createsuperuser # prompts for username, email, password
python manage.py runserver
# visit http://127.0.0.1:8000/admin/
The admin login page loads (verified: /admin/login/ returns HTTP 200, titled "Django administration"),
and once logged in you can list, add, edit, delete and search Doctor records — with no view, template or
form written. That is the headline: a working CRUD interface generated entirely from your model. For a
clinic's receptionist adding patients and booking appointments, this may be all the interface they ever
need.
Making it useful: ModelAdmin
The bare registration works but is plain. You customise the admin per model with a ModelAdmin class, and
a few options transform it from a toy into an operations tool:
@admin.register(Patient)
class PatientAdmin(admin.ModelAdmin):
list_display = ("name", "phone", "city", "created_at") # columns in the list view
list_filter = ("city",) # a sidebar filter
search_fields = ("name", "phone") # a search box over these fields
Each option earns its place (this config is verified to pass manage.py check):
list_display— the columns shown in the list. Without it you see only each row's__str__; with it, staff see the phone, city and creation date at a glance. This is the difference between a list you can work from and one you cannot.list_filter— filters in the right-hand sidebar.list_filter = ("city",)lets staff instantly show only Pune patients — clickable filtering with no code.search_fields— a search box that queries the named fields. Now staff can find a patient by name or phone number.
Three lines, and the patient admin becomes something a busy front desk can actually use — search, filter, and a legible table.
More options you will reach for
As models grow, a handful more options handle the common needs:
@admin.register(Appointment)
class AppointmentAdmin(admin.ModelAdmin):
list_display = ("patient", "doctor", "scheduled_for", "status")
list_filter = ("status", "doctor")
autocomplete_fields = ("patient",) # a search-as-you-type picker, not a giant dropdown
date_hierarchy = "scheduled_for" # drill down by year/month/day
list_select_related = ("patient", "doctor") # avoid N+1 in the list view
autocomplete_fieldsreplaces aForeignKey's default dropdown (which lists every patient — hopeless with thousands) with a search-as-you-type widget. It requires the target model's admin to havesearch_fields— whichPatientAdminabove does, which is why this configuration passes the system check.date_hierarchyadds date drill-down navigation across the top.list_select_relatedappliesselect_relatedto the list query — the admin can suffer the N+1 problem too, and this is the fix, right there in the admin config.
There is much more (readonly_fields, fieldsets to lay out the edit form, inlines to edit related
objects on the same page, custom actions for bulk operations), but list_display, list_filter,
search_fields and autocomplete_fields cover most of what makes an admin usable day to day.
The admin is a real, permission-aware tool — treat it as one
Two things to take seriously:
- The admin respects permissions. Access is gated by the
is_staffflag and per-model add/change/delete permissions (the auth module covers these). You can give the front desk access to patients and appointments but not to user accounts or billing — the admin enforces it. This is what makes it safe to hand to non-developers. - The admin is powerful, which means it is dangerous. A staff user with delete permission can delete
real records. Grant permissions narrowly, and for irreversible or sensitive data consider
readonly_fieldsor removing delete permission for that model. The admin's power is exactly why access to it must be deliberate.
Where the admin stops — and it does stop
The admin is superb for staff managing data, and a poor fit for anything else. Be clear about the line:
- It is not your public-facing UI. It is styled and structured for internal data management, not for patients booking their own appointments. Public and customer-facing pages are views and templates you write (the next module) — never the admin.
- It is not for complex workflows. Multi-step processes, custom business rules, and anything with a specific user experience belong in your own views. Bending the admin into a customer app fights it hard.
- The rule: use the admin for internal CRUD and operations, and build your own views for everything a patient or the public touches. Knowing that boundary is what stops teams from either under-using a free gift or over-stretching it into a product it was never meant to be.
Check your work
How the admin appears. It is enabled by default; register a model (admin.site.register(Model)), create
a superuser, and you get a full CRUD interface at /admin/ with no view/template/form.
What ModelAdmin customises. list_display (columns), list_filter (sidebar filters), search_fields
(a search box) — three lines that make a list workable.
Useful further options. autocomplete_fields (search-as-you-type for FKs; needs the target's
search_fields), date_hierarchy (date drill-down), list_select_related (fix admin N+1).
Why the admin is safe to delegate. It respects is_staff and per-model permissions, so access can be
scoped to exactly the models a role should touch.
Why it is also dangerous. Staff with delete permission can destroy real data — grant permissions
narrowly and use readonly_fields/removed delete for sensitive models.
Where the admin stops. Internal staff CRUD only — not the public UI, not complex workflows; build your own views for anything a patient or the public touches.
Practice
- Register
Doctor, create a superuser, and log in to/admin/. Add and edit a doctor entirely through the admin. - Add a
PatientAdminwithlist_display,list_filterandsearch_fields. Confirm the columns, the sidebar filter, and the search box all work. - Add an
AppointmentAdminwithautocomplete_fields = ("patient",). First run withoutsearch_fieldsonPatientAdminand read the system-check error; addsearch_fieldsand confirm it passes. - Add
list_select_relatedto the appointment admin and, with query logging, confirm the list view stops issuing N+1 queries. - Create a second, non-superuser staff user, grant them permission only on
Patient, and confirm they cannot seeAppointmentin the admin. - Write one sentence each for three features of Nidaan: which belong in the admin, and which need your own views — and why.
Official documentation
- Django — The admin site — The complete
ModelAdminreference. - Django — Writing your first app, part 7 — Customising the admin, step by step.
- Django —
list_displayand friends — Each list-view option.
Next: the ORM in depth — aggregation and annotation.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship