RizTech Academy logo
RizTech Academy
Models and the ORMLesson 6 of 630 min

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_fields replaces a ForeignKey'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 have search_fields — which PatientAdmin above does, which is why this configuration passes the system check.
  • date_hierarchy adds date drill-down navigation across the top.
  • list_select_related applies select_related to 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_staff flag 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_fields or 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

  1. Register Doctor, create a superuser, and log in to /admin/. Add and edit a doctor entirely through the admin.
  2. Add a PatientAdmin with list_display, list_filter and search_fields. Confirm the columns, the sidebar filter, and the search box all work.
  3. Add an AppointmentAdmin with autocomplete_fields = ("patient",). First run without search_fields on PatientAdmin and read the system-check error; add search_fields and confirm it passes.
  4. Add list_select_related to the appointment admin and, with query logging, confirm the list view stops issuing N+1 queries.
  5. Create a second, non-superuser staff user, grant them permission only on Patient, and confirm they cannot see Appointment in the admin.
  6. Write one sentence each for three features of Nidaan: which belong in the admin, and which need your own views — and why.

Official documentation

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