Skip to main content

Admin Interface

Django's admin interface provides a web-based interface for managing your application's data.

Basic Admin Setup

Registering Models

# admin.py
from django.contrib import admin
from .models import Article, Category, Tag

# Simple registration
admin.site.register(Article)
admin.site.register(Category)

# Custom admin class
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
list_display = ['name', 'created_at']
search_fields = ['name']

Creating Superuser

python manage.py createsuperuser

Customizing Admin Classes

List Display and Filtering

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
list_display = ['title', 'author', 'status', 'created_at', 'is_published']
list_filter = ['status', 'created_at', 'author']
search_fields = ['title', 'content']
date_hierarchy = 'created_at'
ordering = ['-created_at']
list_per_page = 25

def is_published(self, obj):
return obj.status == 'published'
is_published.boolean = True
is_published.short_description = 'Published?'

Form Customization

class ArticleAdmin(admin.ModelAdmin):
fields = ['title', 'slug', 'content', 'status']
# or
fieldsets = [
('Basic Information', {
'fields': ['title', 'slug']
}),
('Content', {
'fields': ['content']
}),
('Publishing', {
'fields': ['status', 'featured'],
'classes': ['collapse']
})
]

prepopulated_fields = {'slug': ('title',)}
filter_horizontal = ['tags']
raw_id_fields = ['author']

Inline Models

Tabular and Stacked Inlines

class BookInline(admin.TabularInline):
model = Book
extra = 1
fields = ['title', 'isbn', 'publication_date']

class AuthorAdmin(admin.ModelAdmin):
inlines = [BookInline]
list_display = ['name', 'book_count']

def book_count(self, obj):
return obj.book_set.count()
book_count.short_description = 'Number of Books'

# Stacked inline for detailed forms
class ProfileInline(admin.StackedInline):
model = Profile
can_delete = False
verbose_name_plural = 'Profile'

class UserAdmin(admin.ModelAdmin):
inlines = [ProfileInline]

Custom Actions

Bulk Actions

class ArticleAdmin(admin.ModelAdmin):
actions = ['make_published', 'make_draft']

def make_published(self, request, queryset):
updated = queryset.update(status='published')
self.message_user(
request,
f'{updated} articles were successfully marked as published.'
)
make_published.short_description = 'Mark selected articles as published'

def make_draft(self, request, queryset):
updated = queryset.update(status='draft')
self.message_user(
request,
f'{updated} articles were successfully marked as draft.'
)
make_draft.short_description = 'Mark selected articles as draft'

Admin Forms

Custom Forms

from django import forms
from django.contrib import admin
from .models import Article

class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = '__all__'
widgets = {
'content': forms.Textarea(attrs={'rows': 20, 'cols': 80}),
}

def clean_title(self):
title = self.cleaned_data['title']
if len(title) < 5:
raise forms.ValidationError('Title must be at least 5 characters long')
return title

class ArticleAdmin(admin.ModelAdmin):
form = ArticleForm

Overriding Admin Methods

class ArticleAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
if not change: # Creating new object
obj.author = request.user
super().save_model(request, obj, form, change)

def get_queryset(self, request):
qs = super().get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(author=request.user)

def has_change_permission(self, request, obj=None):
if obj is None:
return True
return obj.author == request.user or request.user.is_superuser

Advanced Customization

Custom Admin Views

from django.urls import path
from django.shortcuts import render
from django.contrib.admin.views.decorators import staff_member_required

class ArticleAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super().get_urls()
custom_urls = [
path('statistics/', self.admin_site.admin_view(self.statistics_view), name='article_statistics'),
]
return custom_urls + urls

def statistics_view(self, request):
context = {
'title': 'Article Statistics',
'total_articles': Article.objects.count(),
'published_articles': Article.objects.filter(status='published').count(),
'draft_articles': Article.objects.filter(status='draft').count(),
}
return render(request, 'admin/article_statistics.html', context)

Custom Admin Site

# admin.py
from django.contrib.admin import AdminSite
from django.urls import path

class MyAdminSite(AdminSite):
site_header = 'My Administration'
site_title = 'My Admin Portal'
index_title = 'Welcome to My Administration Portal'

def get_urls(self):
urls = super().get_urls()
custom_urls = [
path('dashboard/', self.admin_view(self.dashboard_view), name='dashboard'),
]
return custom_urls + urls

def dashboard_view(self, request):
context = {
'title': 'Dashboard',
'user_count': User.objects.count(),
'article_count': Article.objects.count(),
}
return render(request, 'admin/dashboard.html', context)

my_admin_site = MyAdminSite(name='myadmin')
my_admin_site.register(Article, ArticleAdmin)

Admin Templates

Customizing Admin Templates

<!-- templates/admin/base_site.html -->
{% extends "admin/base.html" %} {% block title %}{{ title }} | My Site Admin{%
endblock %} {% block branding %}
<h1 id="site-name">
<a href="{% url 'admin:index' %}">My Site Administration</a>
</h1>
{% endblock %} {% block nav-global %}{% endblock %}
<!-- templates/admin/article_statistics.html -->
{% extends "admin/base_site.html" %} {% load i18n admin_urls static admin_modify
%} {% block content %}
<div class="module">
<h2>Article Statistics</h2>
<table>
<tr>
<td>Total Articles:</td>
<td>{{ total_articles }}</td>
</tr>
<tr>
<td>Published Articles:</td>
<td>{{ published_articles }}</td>
</tr>
<tr>
<td>Draft Articles:</td>
<td>{{ draft_articles }}</td>
</tr>
</table>
</div>
{% endblock %}

Permissions in Admin

Model Permissions

class ArticleAdmin(admin.ModelAdmin):
def has_add_permission(self, request):
return request.user.has_perm('myapp.add_article')

def has_change_permission(self, request, obj=None):
if obj is None:
return request.user.has_perm('myapp.change_article')
return obj.author == request.user or request.user.is_superuser

def has_delete_permission(self, request, obj=None):
if obj is None:
return request.user.has_perm('myapp.delete_article')
return request.user.is_superuser

def has_view_permission(self, request, obj=None):
return request.user.has_perm('myapp.view_article')

Admin Groups

# Create admin groups with specific permissions
from django.contrib.auth.models import Group, Permission

# Create editor group
editors = Group.objects.create(name='Editors')

# Add specific permissions
permissions = Permission.objects.filter(
content_type__app_label='myapp',
codename__in=['add_article', 'change_article', 'view_article']
)
editors.permissions.set(permissions)

# Add users to group
user.groups.add(editors)

Admin Media and Assets

Adding Custom CSS/JS

class ArticleAdmin(admin.ModelAdmin):
class Media:
css = {
'all': ('admin/css/custom.css',)
}
js = ('admin/js/custom.js',)
/* static/admin/css/custom.css */
.field-content textarea {
height: 400px;
}

.dashboard-stats {
background: #f8f9fa;
padding: 20px;
border-radius: 5px;
}

Admin Security

Rate Limiting Admin

# settings.py
INSTALLED_APPS = [
'django_ratelimit',
# ... other apps
]

# Protect admin login
from django_ratelimit.decorators import ratelimit
from django.contrib.admin.views.decorators import staff_member_required

@ratelimit(key='ip', rate='5/m', method='POST')
def admin_login(request):
return admin.site.login(request)

Admin Logging

# Log admin actions
import logging

logger = logging.getLogger('admin_actions')

class ArticleAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
if change:
logger.info(f'User {request.user} updated article {obj.id}')
else:
logger.info(f'User {request.user} created article {obj.id}')
super().save_model(request, obj, form, change)