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.
The simplest way to register a model:
# admin.py
from django.contrib import admin
from .models import Article
# Simple registration
admin.site.register(Article)
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])
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
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'
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'
]
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'
]
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
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']
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()
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']
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
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'
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]
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]
@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'
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
# 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)
# 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)
# 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)
@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
@admin.register(SimpleModel)
class SimpleModelAdmin(admin.ModelAdmin):
"""Minimal admin configuration"""
list_display = ['__str__']
search_fields = ['name']
@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']
list_select_related for foreign keysget_queryset() for complex optimizationslist_per_page for large datasetsshort_description for custom methodsNow 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.
Enabling the Admin
Django's admin interface comes built-in but requires some initial setup. This chapter covers everything you need to know to get the admin interface up and running.
Customizing Admin Display
Django's admin interface is highly customizable. This chapter covers advanced techniques for creating a polished, user-friendly admin experience tailored to your specific needs.