Skip to content

Chapter 10: Django Admin Interface

10.1 Admin Basics

Create Superuser

Django admin is a powerful CRUD interface that lets admin users manage website content. First, create a superuser to access the admin:

bash
# Create superuser
python manage.py createsuperuser

# Enter username, email, and password when prompted
Username (leave blank to use 'yourname'): admin
Email address: admin@example.com
Password: ********
Password (again): ********
Superuser created successfully.

Create superuser programmatically:

python
# In Django shell
from django.contrib.auth.models import User

# Method 1: Use create_superuser
user = User.objects.create_superuser(
    username='admin',
    email='admin@example.com',
    password='securepassword'
)

# Method 2: Create user then set superuser permissions
user = User.objects.create_user(
    username='admin',
    email='admin@example.com',
    password='securepassword'
)
user.is_staff = True
user.is_superuser = True
user.save()

Auto-create superuser during deployment:

python
# management/commands/create_admin.py
from django.core.management.base import BaseCommand
from django.contrib.auth.models import User
import os

class Command(BaseCommand):
    help = 'Create a superuser if it does not exist'

    def handle(self, *args, **options):
        username = os.environ.get('DJANGO_SUPERUSER_USERNAME', 'admin')
        email = os.environ.get('DJANGO_SUPERUSER_EMAIL', 'admin@example.com')
        password = os.environ.get('DJANGO_SUPERUSER_PASSWORD', 'admin')
        
        if not User.objects.filter(username=username).exists():
            User.objects.create_superuser(username, email, password)
            self.stdout.write(
                self.style.SUCCESS(f'Superuser {username} created successfully')
            )
        else:
            self.stdout.write(
                self.style.WARNING(f'Superuser {username} already exists')
            )

Register Models

To make models visible in admin, register them in the app's admin.py:

python
# models.py
from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)
    description = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        verbose_name = "Category"
        verbose_name_plural = "Categories"
    
    def __str__(self):
        return self.name

class Tag(models.Model):
    name = models.CharField(max_length=50)
    slug = models.SlugField(unique=True)
    
    class Meta:
        verbose_name = "Tag"
        verbose_name_plural = "Tags"
    
    def __str__(self):
        return self.name

class Article(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
    tags = models.ManyToManyField(Tag, blank=True)
    content = models.TextField()
    excerpt = models.TextField(blank=True)
    is_published = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        verbose_name = "Article"
        verbose_name_plural = "Articles"
        ordering = ['-created_at']
    
    def __str__(self):
        return self.title

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

# Basic registration
admin.site.register(Category)
admin.site.register(Tag)

# Register with decorator
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'category', 'is_published', 'created_at']
    list_filter = ['is_published', 'category', 'created_at']
    search_fields = ['title', 'content']
    prepopulated_fields = {'slug': ('title',)}
    date_hierarchy = 'created_at'

Admin Interface Customization

Customize admin interface for better usability:

python
# admin.py
from django.contrib import admin
from django.utils.html import format_html
from .models import Category, Tag, Article

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ['name', 'slug', 'article_count', 'created_at']
    list_filter = ['created_at']
    search_fields = ['name', 'description']
    prepopulated_fields = {'slug': ('name',)}
    ordering = ['name']
    
    def article_count(self, obj):
        return obj.article_set.count()
    article_count.short_description = 'Article Count'

@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
    list_display = ['name', 'slug', 'article_count']
    search_fields = ['name']
    prepopulated_fields = {'slug': ('name',)}
    
    def article_count(self, obj):
        return obj.article_set.count()
    article_count.short_description = 'Article Count'

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    # List display
    list_display = [
        'title', 'author', 'category', 'is_published', 
        'created_at', 'preview_link'
    ]
    list_filter = ['is_published', 'category', 'tags', 'created_at', 'author']
    search_fields = ['title', 'content', 'excerpt']
    ordering = ['-created_at']
    date_hierarchy = 'created_at'
    
    # Edit page
    fieldsets = (
        ('Basic Info', {
            'fields': ('title', 'slug', 'author', 'excerpt')
        }),
        ('Content', {
            'fields': ('content',),
            'classes': ('wide',)
        }),
        ('Category and Tags', {
            'fields': ('category', 'tags')
        }),
        ('Publish Settings', {
            'fields': ('is_published',),
            'classes': ('collapse',)
        }),
    )
    
    # Prepopulated fields
    prepopulated_fields = {'slug': ('title',)}
    
    # Read-only fields
    readonly_fields = ['created_at', 'updated_at']
    
    # Foreign key search in filters
    autocomplete_fields = ['author', 'category']
    
    def preview_link(self, obj):
        if obj.is_published:
            return format_html(
                '<a href="{}" target="_blank">Preview</a>',
                obj.get_absolute_url()
            )
        return "Not Published"
    preview_link.short_description = "Preview"
    
    # Custom queryset
    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if request.user.is_superuser:
            return qs
        # Regular users can only see their own articles
        return qs.filter(author=request.user)
    
    # Custom save logic
    def save_model(self, request, obj, form, change):
        if not change:  # Set author when creating new
            obj.author = request.user
        super().save_model(request, obj, form, change)

Field Display Configuration

Configure how fields appear in admin:

python
# admin.py
from django.contrib import admin
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from .models import Article

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = [
        'title', 'author', 'category', 'is_published', 
        'created_at', 'word_count', 'status_badge'
    ]
    
    # Custom field display
    def word_count(self, obj):
        return len(obj.content.split())
    word_count.short_description = 'Word Count'
    word_count.admin_order_field = 'content'  # Sortable
    
    # Show status badge
    def status_badge(self, obj):
        if obj.is_published:
            return format_html(
                '<span style="background-color: #28a745; color: white; padding: 4px 8px; border-radius: 4px;">Published</span>'
            )
        else:
            return format_html(
                '<span style="background-color: #ffc107; color: black; padding: 4px 8px; border-radius: 4px;">Draft</span>'
            )
    status_badge.short_description = 'Status'
    
    # Show rich text content preview
    def content_preview(self, obj):
        # Get first 200 characters
        preview = obj.content[:200]
        if len(obj.content) > 200:
            preview += '...'
        return mark_safe(preview)
    content_preview.short_description = 'Content Preview'
    
    # Conditional field display
    def get_list_display(self, request):
        # Superusers see all fields
        if request.user.is_superuser:
            return self.list_display + ['content_preview']
        # Regular users don't see some fields
        return [field for field in self.list_display if field != 'author']

With these configurations, you can create a powerful and user-friendly Django admin interface.

Summary

Django admin basic features include:

  1. ✅ Create superuser via command line or programmatically
  2. ✅ Register models in admin.py to make them visible
  3. ✅ Customize admin interface for better user experience
  4. ✅ Configure field display and list filters
  5. ✅ Use decorators and ModelAdmin class for advanced customization

Mastering admin basics helps you quickly build content management systems.

Next

We will learn advanced admin configuration techniques.

10.2 Advanced Admin Configuration →

Contents

Return to Course Outline

Released under the [BY-NC-ND License](https://creativecommons.org/licenses/by-nc-nd/4.0/deed.en).