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)