Django Admin

Introduction

Django ships a production-ready admin site at /admin/ for staff to create, edit, and delete model rows through the browser. With a few lines in admin.py, your Post model from earlier chapters becomes searchable, filterable, and editable. This chapter creates a superuser, registers models, customizes ModelAdmin, and covers security basics for production.

Prerequisites

Enable Admin (Already Default)

config/settings.py includes:

python
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    ...
]

config/urls.py:

python
from django.contrib import admin
from django.urls import include, path
 
urlpatterns = [
    path("admin/", admin.site.urls),
    path("", include("blog.urls")),
]

No extra install step—admin is part of django.contrib.

Create a Superuser

bash
python manage.py createsuperuser

Prompts for username, email (optional), and password. Password input is hidden; Django stores a hash, never plaintext.

Visit http://127.0.0.1:8000/admin/ and log in. You should see Authentication and Authorization (Users, Groups) and your apps once models are registered.

Register Models

Basic registration

blog/admin.py:

python
from django.contrib import admin
 
from .models import Post, Tag
 
admin.site.register(Post)
admin.site.register(Tag)

Refresh admin—Posts and Tags appear under BLOG.

Decorator style (preferred)

Tip

One admin.py File

The snippets below build up a single PostAdmin / TagAdmin—merge them into one blog/admin.py, not multiple duplicate @admin.register(Post) blocks.

Customize PostAdmin

python
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ["title", "author", "published", "created_at"]
    list_filter = ["published", "created_at", "tags"]
    search_fields = ["title", "body", "slug"]
    prepopulated_fields = {"slug": ("title",)}
    readonly_fields = ["created_at", "updated_at"]
    date_hierarchy = "created_at"
    list_editable = ["published"]
    ordering = ["-created_at"]
    filter_horizontal = ["tags"]
OptionEffect
list_displayColumns on changelist page
list_filterSidebar filters
search_fieldsSearch box (icontains)
prepopulated_fieldsAuto slug from title
readonly_fieldsShow but do not edit on form
date_hierarchyDrill-down by date
list_editableEdit column inline on list
filter_horizontalBetter UI for M2M

Fieldsets and Layout

python
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    fieldsets = [
        (None, {"fields": ["title", "slug", "author"]}),
        ("Content", {"fields": ["summary", "body", "tags"]}),
        ("Meta", {"fields": ["published", "created_at", "updated_at"]}),
    ]
    readonly_fields = ["created_at", "updated_at"]

Collapsible sections improve long forms.

For many-to-many tags, filter_horizontal (above) is usually enough. Use an inline only when you need extra fields on the junction table:

python
class PostTagInline(admin.TabularInline):
    model = Post.tags.through  # M2M through table
    extra = 1

For ForeignKey from another model to Post:

python
class CommentInline(admin.StackedInline):
    model = Comment
    extra = 0

Add inlines = [CommentInline] to PostAdmin.

Permissions in Admin

Django creates default permissions per model: add, change, delete, view.

  • Superusers bypass all checks
  • Staff users (is_staff=True) need explicit permissions via Groups or user permissions

Assign in admin: Users → user → Permissions or Groups.

Preview of decorator-based views: Authentication.

Admin Actions

Bulk operations on selected rows:

python
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    ...
    actions = ["make_published", "make_draft"]
 
    @admin.action(description="Mark selected posts as published")
    def make_published(self, request, queryset):
        queryset.update(published=True)

Customize Admin Site Header

config/urls.py or blog/admin.py:

python
admin.site.site_header = "Hello Django Admin"
admin.site.site_title = "Blog Admin"
admin.site.index_title = "Site management"

Change Admin URL (Optional)

Obscurity is not security, but non-default paths reduce noise:

python
path("secret-mgmt/", admin.site.urls),

Combine with network restrictions in production.

Admin Security

PracticeWhy
Strong superuser passwordsAdmin full DB access
DEBUG=False in productionNo traceback leak
Limit /admin/ by IP or VPNNginx allow/deny
HTTPS onlyProtect session cookie
Keep Django updatedSecurity patches
is_staff only for trusted usersStaff can access admin login

Nginx example (concept)—full deploy in chapter 23:

nginx
location /admin/ {
    allow 10.0.0.0/8;
    deny all;
    proxy_pass http://127.0.0.1:8000;
}

Third-party 2FA for admin (django-otp, etc.)—consider for high-value sites.

Admin vs Custom Views

Use AdminBuild custom views
Internal editors, CRUD on many modelsPublic-facing UX, branding
Rapid prototypingComplex workflows, wizards
Permission model already fitsFine-grained object rules

Blog public pages use templates and forms—Forms chapter. Admin complements, not replaces, your site.

Autocomplete for ForeignKey

Large user lists: enable search on User admin, then:

python
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    autocomplete_fields = ["author"]

Requires search_fields on related model’s ModelAdmin.

Common Problems

SymptomFix
Model missing from adminRegister in admin.py; restart server
Forbidden after loginUser needs is_staff=True
Slug not auto-fillingprepopulated_fields; save after typing title
M2M not savingSave parent first; use filter_horizontal
Slow changelistlist_select_related, pagination defaults

Post-Chapter Checklist

  • Superuser created; /admin/ login works
  • Post registered with list_display, search_fields, list_filter
  • prepopulated_fields for slug from title
  • You understand staff vs superuser and permissions
  • You know production hardening options for admin URL

FAQ## FAQ

Admin in Chinese?

Set LANGUAGE_CODE = "zh-hans" and ensure locale middleware; translate with Django i18n for full UI.

Hide a model from admin?

Simply do not register it.

Register same model twice?

Error—use one ModelAdmin class.

Custom admin template?

Override admin/change_list.html in project templates/—advanced theming.

Export CSV from admin?

Use third-party packages (django-import-export) or custom actions.