Customizing the Admin

The previous lesson registered TaskFlow's models with a single line each — admin.site.register(Task) — and got a fully working admin in return. But "working" and "pleasant to use every day" aren't the same thing. This lesson is about closing that gap: subclassing admin.ModelAdmin to control exactly which columns show up, adding filters and a search box, and even making some fields editable directly from the list, without opening a single edit form.

Subclassing ModelAdmin

Every customization in this lesson starts the same way: instead of registering the bare model, you define a class that inherits from admin.ModelAdmin and set attributes on it. Django reads those attributes and changes how the admin behaves for that model accordingly:

# taskflow/admin.py
from django.contrib import admin
from .models import Task

@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
    pass # attributes go here

list_display: Choosing List View Columns

By default, the admin's list view shows just one column — usually the model's string representation. list_display lets you choose exactly which fields appear as columns instead:

Example: Showing Task Details as Columns

class TaskAdmin(admin.ModelAdmin):
    list_display = ('title', 'project', 'priority', 'completed')

The task list now shows four real columns at a glance — title, which project it belongs to, its priority, and whether it's completed — instead of forcing you to open each task individually just to see that information.

list_filter: A Sidebar Filter

list_filter adds a filter sidebar to the right of the list view, letting you narrow the visible rows by a field's value without writing any search query at all:

Example: Filtering by Priority and Completion

class TaskAdmin(admin.ModelAdmin):
    list_display = ('title', 'project', 'priority', 'completed')
    list_filter = ('priority', 'completed')

With this in place, the admin sidebar offers one-click links for each priority level and for completed versus incomplete tasks, letting you drill down to, say, "every high-priority task that isn't done yet" in two clicks.

search_fields: Adding a Search Box

search_fields puts a search box above the list view, matching against whichever fields you list. Searching TaskFlow's tasks by title is a natural fit:

class TaskAdmin(admin.ModelAdmin):
    search_fields = ('title',)

Typing into that box performs a case-insensitive partial match against title, similar in spirit to an icontains lookup — you don't need to type a task's exact, full title to find it.

list_editable: Editing Directly From the List

Opening a full edit form just to flip one checkbox is slow once you're managing dozens of tasks. list_editable turns specific columns into editable inputs right inside the list view itself — change several rows, then save them all in one submission:

Example: Toggling completed Inline

class TaskAdmin(admin.ModelAdmin):
    list_display = ('title', 'project', 'priority', 'completed')
    list_editable = ('completed',)
Note: Every field in list_editable must also appear in list_display — the admin needs to be showing a column before it can make that column editable.

ordering: The Default Sort Order

ordering controls the order rows appear in when the list view first loads, before any column header is clicked to re-sort. Showing the newest or most urgent tasks first is usually more useful than whatever order the database happens to return rows in:

class TaskAdmin(admin.ModelAdmin):
    ordering = ('-due_date',)

The leading - reverses the sort, exactly the same convention used with .order_by() on a QuerySet — tasks with the furthest-out due date appear first here.

readonly_fields: Showing a Field Without Allowing Edits

Some fields are useful to see on the edit form but shouldn't ever be changed by hand — a created_at timestamp is the classic example. readonly_fields keeps a field visible on the detail form while making it impossible to edit:

class TaskAdmin(admin.ModelAdmin):
    readonly_fields = ('created_at',)

Putting It All Together

None of these options are mutually exclusive — a single ModelAdmin subclass typically combines several of them at once. Here's a more complete TaskAdmin for TaskFlow, folding in everything from this lesson:

Example: A Polished TaskAdmin

# taskflow/admin.py
from django.contrib import admin
from .models import Task

@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
    list_display = ('title', 'project', 'priority', 'completed')
    list_filter = ('priority', 'completed')
    search_fields = ('title',)
    list_editable = ('completed',)
    ordering = ('-due_date',)
    readonly_fields = ('created_at',)

That one class turns the bare, single-column admin list from the previous lesson into something genuinely usable day-to-day — sortable, filterable, searchable, and editable in place, all from declarative attributes rather than any custom view code.

Note: These ModelAdmin snippets are correct and exactly what you'd write in a real project's admin.py — but this website's own project doesn't run a live Django server, so the admin UI itself isn't rendered on this page.
Next up: Admin Actions & Inlines covers bulk actions on multiple selected rows at once, and editing related models like Tags directly inside a Task's own admin page.