Admin Site

Registering Models

To make your models available in the Django admin interface, you need to register them. This chapter covers various ways to register models and basic configuration options.

Registering Models

To make your models available in the Django admin interface, you need to register them. This chapter covers various ways to register models and basic configuration options.

Basic Model Registration

Simple Registration

The simplest way to register a model:

# admin.py
from django.contrib import admin
from .models import Article

# Simple registration
admin.site.register(Article)

Registration with ModelAdmin

For more control, use a ModelAdmin class:

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

class ArticleAdmin(admin.ModelAdmin):
    """Admin configuration for Article model"""
    pass

# Register with custom admin class
admin.site.register(Article, ArticleAdmin)

# Multiple registrations
admin.site.register([Category, Tag])

Using Decorators

A cleaner approach using decorators:

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

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    """Article admin configuration"""
    pass

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    """Category admin configuration"""
    pass

@admin.register(Author)
class AuthorAdmin(admin.ModelAdmin):
    """Author admin configuration"""
    pass

Basic ModelAdmin Configuration

List Display

Control which fields appear in the list view:

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = [
        'title',
        'author',
        'category',
        'status',
        'created_at',
        'is_published'
    ]
    
    def is_published(self, obj):
        """Custom method for list display"""
        return obj.status == 'published'
    
    is_published.boolean = True  # Display as boolean icon
    is_published.short_description = 'Published'

List Filters

Add filtering options to the sidebar:

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'category', 'created_at']
    list_filter = [
        'status',
        'category',
        'created_at',
        'author'
    ]

Search Fields

Enable search functionality:

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'category']
    search_fields = [
        'title',
        'content',
        'author__username',  # Search in related model
        'author__email'
    ]

Ordering

Set default ordering for the list view:

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'created_at']
    ordering = ['-created_at', 'title']  # Newest first, then by title

Advanced Registration Patterns

Conditional Registration

Register models based on conditions:

# admin.py
from django.contrib import admin
from django.conf import settings
from .models import Article, DebugModel

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'created_at']

# Only register debug model in development
if settings.DEBUG:
    @admin.register(DebugModel)
    class DebugModelAdmin(admin.ModelAdmin):
        list_display = ['name', 'created_at']

Dynamic Registration

Register models dynamically:

# admin.py
from django.contrib import admin
from django.apps import apps

def register_all_models():
    """Register all models from the current app"""
    app_models = apps.get_app_config('myapp').get_models()
    
    for model in app_models:
        if not admin.site.is_registered(model):
            admin.site.register(model)

# Call the function
register_all_models()

Custom Admin Site Registration

Register models with a custom admin site:

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

class MyAdminSite(AdminSite):
    site_header = 'My Custom Admin'
    site_title = 'My Admin'

# Create custom admin site
my_admin_site = MyAdminSite(name='myadmin')

# Register models with custom site
@admin.register(Article, site=my_admin_site)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'created_at']

@admin.register(Category, site=my_admin_site)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ['name', 'description']

Foreign Key Display

Customize how foreign keys are displayed:

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'get_author_email', 'category']
    
    def get_author_email(self, obj):
        """Display author's email"""
        return obj.author.email if obj.author else '-'
    
    get_author_email.short_description = 'Author Email'
    get_author_email.admin_order_field = 'author__email'  # Enable sorting

Many-to-Many Display

Display many-to-many relationships:

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'get_tags']
    
    def get_tags(self, obj):
        """Display article tags"""
        return ', '.join([tag.name for tag in obj.tags.all()])
    
    get_tags.short_description = 'Tags'

Optimize database queries for related fields:

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'category', 'get_tags']
    list_select_related = ['author', 'category']  # Use select_related
    
    def get_queryset(self, request):
        """Optimize queryset with prefetch_related"""
        queryset = super().get_queryset(request)
        return queryset.prefetch_related('tags')
    
    def get_tags(self, obj):
        return ', '.join([tag.name for tag in obj.tags.all()])
    get_tags.short_description = 'Tags'

Model Registration with Inline Editing

Tabular Inlines

Edit related models inline:

from django.contrib import admin
from .models import Article, Comment

class CommentInline(admin.TabularInline):
    """Inline editing for comments"""
    model = Comment
    extra = 1  # Number of empty forms to display
    fields = ['author', 'content', 'is_approved']

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'created_at']
    inlines = [CommentInline]

Stacked Inlines

For more complex related models:

class ArticleImageInline(admin.StackedInline):
    """Stacked inline for article images"""
    model = ArticleImage
    extra = 0
    fields = ['image', 'caption', 'alt_text']

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'created_at']
    inlines = [CommentInline, ArticleImageInline]

Custom Field Display

Custom Methods in List Display

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = [
        'title',
        'author',
        'word_count',
        'reading_time',
        'status_badge'
    ]
    
    def word_count(self, obj):
        """Calculate and display word count"""
        return len(obj.content.split()) if obj.content else 0
    
    word_count.short_description = 'Words'
    word_count.admin_order_field = 'content'
    
    def reading_time(self, obj):
        """Estimate reading time"""
        words = len(obj.content.split()) if obj.content else 0
        minutes = max(1, words // 200)  # Assume 200 words per minute
        return f"{minutes} min"
    
    reading_time.short_description = 'Reading Time'
    
    def status_badge(self, obj):
        """Display status with color coding"""
        colors = {
            'draft': 'orange',
            'published': 'green',
            'archived': 'gray'
        }
        color = colors.get(obj.status, 'black')
        return format_html(
            '<span style="color: {};">{}</span>',
            color,
            obj.get_status_display()
        )
    
    status_badge.short_description = 'Status'

HTML in Admin Display

from django.utils.html import format_html
from django.utils.safestring import mark_safe

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'thumbnail', 'author_link']
    
    def thumbnail(self, obj):
        """Display thumbnail image"""
        if obj.featured_image:
            return format_html(
                '<img src="{}" width="50" height="50" style="border-radius: 5px;" />',
                obj.featured_image.url
            )
        return '-'
    
    thumbnail.short_description = 'Image'
    
    def author_link(self, obj):
        """Link to author's profile"""
        if obj.author:
            return format_html(
                '<a href="/admin/auth/user/{}/change/">{}</a>',
                obj.author.id,
                obj.author.username
            )
        return '-'
    
    author_link.short_description = 'Author'
    author_link.allow_tags = True

Bulk Registration Utilities

Registration Helper Function

# admin.py
from django.contrib import admin

def register_models(models, admin_class=None):
    """Helper function to register multiple models"""
    for model in models:
        if admin_class:
            admin.site.register(model, admin_class)
        else:
            admin.site.register(model)

# Usage
from .models import Category, Tag, Status

# Register multiple models with default admin
register_models([Category, Tag, Status])

# Register with custom admin class
class SimpleAdmin(admin.ModelAdmin):
    list_display = ['__str__', 'id']

register_models([Category, Tag], SimpleAdmin)

Auto-Registration Decorator

# utils.py
from django.contrib import admin

def auto_register(admin_class=None):
    """Decorator to automatically register models"""
    def decorator(model_class):
        if admin_class:
            admin.site.register(model_class, admin_class)
        else:
            admin.site.register(model_class)
        return model_class
    return decorator

# Usage in models.py
from .utils import auto_register

@auto_register()
class Category(models.Model):
    name = models.CharField(max_length=100)

@auto_register(admin_class=CustomAdmin)
class Article(models.Model):
    title = models.CharField(max_length=200)

Testing Model Registration

Admin Registration Tests

# tests.py
from django.test import TestCase
from django.contrib import admin
from .models import Article, Category
from .admin import ArticleAdmin, CategoryAdmin

class AdminRegistrationTest(TestCase):
    """Test admin registration"""
    
    def test_models_registered(self):
        """Test that models are registered"""
        self.assertIn(Article, admin.site._registry)
        self.assertIn(Category, admin.site._registry)
    
    def test_admin_classes(self):
        """Test correct admin classes are used"""
        self.assertIsInstance(admin.site._registry[Article], ArticleAdmin)
        self.assertIsInstance(admin.site._registry[Category], CategoryAdmin)
    
    def test_list_display(self):
        """Test list display configuration"""
        article_admin = admin.site._registry[Article]
        expected_fields = ['title', 'author', 'created_at']
        self.assertEqual(article_admin.list_display, expected_fields)

Common Registration Patterns

Read-Only Models

@admin.register(LogEntry)
class LogEntryAdmin(admin.ModelAdmin):
    """Read-only admin for log entries"""
    
    list_display = ['timestamp', 'user', 'action', 'object_repr']
    list_filter = ['timestamp', 'action']
    search_fields = ['object_repr', 'change_message']
    
    # Make it read-only
    def has_add_permission(self, request):
        return False
    
    def has_change_permission(self, request, obj=None):
        return False
    
    def has_delete_permission(self, request, obj=None):
        return False

Simplified Admin

@admin.register(SimpleModel)
class SimpleModelAdmin(admin.ModelAdmin):
    """Minimal admin configuration"""
    
    list_display = ['__str__']
    search_fields = ['name']

Advanced Configuration

@admin.register(ComplexModel)
class ComplexModelAdmin(admin.ModelAdmin):
    """Full-featured admin configuration"""
    
    list_display = ['title', 'status', 'author', 'created_at']
    list_filter = ['status', 'created_at', 'author']
    search_fields = ['title', 'content']
    ordering = ['-created_at']
    list_per_page = 25
    
    fieldsets = (
        ('Basic Information', {
            'fields': ('title', 'slug', 'author')
        }),
        ('Content', {
            'fields': ('content', 'excerpt')
        }),
        ('Metadata', {
            'fields': ('status', 'tags', 'created_at'),
            'classes': ('collapse',)
        })
    )
    
    readonly_fields = ['created_at', 'updated_at']
    filter_horizontal = ['tags']

Best Practices

Organization

  • Keep admin configuration close to models
  • Use descriptive names for custom methods
  • Group related admin classes together

Performance

  • Use list_select_related for foreign keys
  • Implement get_queryset() for complex optimizations
  • Limit list_per_page for large datasets

User Experience

  • Provide meaningful short_description for custom methods
  • Use appropriate field ordering
  • Add helpful filters and search fields

Security

  • Be careful with HTML output in admin methods
  • Validate permissions for sensitive operations
  • Use read-only fields for calculated values

Next Steps

Now that you know how to register models, let's explore how to customize the admin display to create a more user-friendly and efficient interface.