Courses / Django Full Stack Web Development
Lesson 9 of 13
Django Admin Customization: A Practical Tutorial
Go beyond the default Django admin: customize ModelAdmin classes, list views, search, filters, inline editing, and admin actions to build a usable backoffice your team will actually enjoy.
What you'll learn
- ✓How ModelAdmin classes shape the admin UI
- ✓Customizing list_display, filters, and search
- ✓Using inlines for related models
- ✓Building admin actions for bulk operations
- ✓Overriding templates and form fields safely
- ✓Common pitfalls when customizing admin at scale
Prerequisites
- •Comfort with Django models and migrations
The Django admin is one of the framework’s killer features, but the out-of-the-box version is rarely what you want to ship to internal users. With a few targeted customizations you can turn it into a real backoffice tool.
What and Why
Django’s admin is a generated CRUD UI for your models. By registering each model with a ModelAdmin subclass, you can change which columns appear, what filters are available, which fields are editable inline, and what bulk actions users can run. It is not a replacement for a customer-facing app, but for ops, support, and content teams it is unbeatable.
Mental Model
Think of the admin as three layers stacked on top of your ORM. The model layer defines data. The ModelAdmin layer defines how that data is presented. The template and form layer renders it as HTML. Most customizations live in the middle layer; you only drop down to templates when you genuinely need to.
Hands-on Example
Suppose you have an Order model with a customer, total, status, and many OrderItem rows. A useful admin might look like this:
from django.contrib import admin
from .models import Order, OrderItem
class OrderItemInline(admin.TabularInline):
model = OrderItem
extra = 0
readonly_fields = ("unit_price",)
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = ("id", "customer", "status", "total", "created_at")
list_filter = ("status", "created_at")
search_fields = ("id", "customer__email")
ordering = ("-created_at",)
inlines = [OrderItemInline]
actions = ["mark_as_shipped"]
@admin.action(description="Mark selected orders as shipped")
def mark_as_shipped(self, request, queryset):
updated = queryset.update(status="shipped")
self.message_user(request, f"{updated} orders marked shipped.")
With this in place, support staff can find an order by email, filter by status, and ship a whole batch at once.
Common Pitfalls
The first pitfall is list_display with expensive computed methods. Each row calls them, so adding def total_with_tax(self, obj) that hits the database multiplies queries. Annotate in get_queryset instead.
The second is overusing raw_id_fields versus autocomplete_fields. For tables with millions of rows, the default select widget will freeze the admin; switch to autocomplete_fields and register a search_fields on the related model’s admin.
The third is forgetting permissions. Custom actions still respect has_change_permission, but custom views you wire into get_urls do not unless you check explicitly.
Finally, do not override admin templates wholesale. You will inherit a maintenance burden every Django release. Prefer template blocks or change_form_template for the specific page that needs tweaks.
Practical Tips
Use @admin.display(ordering="...", description="...") to make computed columns sortable and well-labeled. Use readonly_fields to expose internal IDs without letting staff edit them. Group fields with fieldsets for long forms. For relationship-heavy models, combine list_select_related and list_prefetch_related (3.1+) to keep the changelist snappy.
If your admin grows beyond a dozen models, split admin.py per app and consider a custom AdminSite subclass with a project-wide header and grouped navigation.
Wrap-up
The Django admin scales from prototype to internal product as long as you treat ModelAdmin as your main customization surface. Tune list_display, filters, search, and actions first; reach for templates and custom views only when the standard knobs run out. Done well, your admin becomes the tool your team opens every morning instead of avoiding.
Progress is saved locally to your browser.