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
- Database Migrations —
Post(and related models) migrated - Models and ORM Basics
- Dev server:
python manage.py runserver
Enable Admin (Already Default)
config/settings.py includes:
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
...
]config/urls.py:
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
python manage.py createsuperuserPrompts 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:
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
@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"]| Option | Effect |
|---|---|
list_display | Columns on changelist page |
list_filter | Sidebar filters |
search_fields | Search box (icontains) |
prepopulated_fields | Auto slug from title |
readonly_fields | Show but do not edit on form |
date_hierarchy | Drill-down by date |
list_editable | Edit column inline on list |
filter_horizontal | Better UI for M2M |
Fieldsets and Layout
@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.
Inline Related Objects (Optional)
For many-to-many tags, filter_horizontal (above) is usually enough. Use an inline only when you need extra fields on the junction table:
class PostTagInline(admin.TabularInline):
model = Post.tags.through # M2M through table
extra = 1For ForeignKey from another model to Post:
class CommentInline(admin.StackedInline):
model = Comment
extra = 0Add 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:
@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:
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:
path("secret-mgmt/", admin.site.urls),Combine with network restrictions in production.
Admin Security
| Practice | Why |
|---|---|
| Strong superuser passwords | Admin full DB access |
DEBUG=False in production | No traceback leak |
Limit /admin/ by IP or VPN | Nginx allow/deny |
| HTTPS only | Protect session cookie |
| Keep Django updated | Security patches |
is_staff only for trusted users | Staff can access admin login |
Nginx example (concept)—full deploy in chapter 23:
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 Admin | Build custom views |
|---|---|
| Internal editors, CRUD on many models | Public-facing UX, branding |
| Rapid prototyping | Complex workflows, wizards |
| Permission model already fits | Fine-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:
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
autocomplete_fields = ["author"]Requires search_fields on related model’s ModelAdmin.
Common Problems
| Symptom | Fix |
|---|---|
| Model missing from admin | Register in admin.py; restart server |
Forbidden after login | User needs is_staff=True |
| Slug not auto-filling | prepopulated_fields; save after typing title |
| M2M not saving | Save parent first; use filter_horizontal |
| Slow changelist | list_select_related, pagination defaults |
Post-Chapter Checklist
- Superuser created;
/admin/login works -
Postregistered withlist_display,search_fields,list_filter -
prepopulated_fieldsfor 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.