Django Admin Customization
Django Admin is an auto-generated admin interface based on your models. CRUD is available immediately with no extra code.
Basic Admin Registration
from django.contrib import admin
from .models import Category, Product, Review
admin.site.register(Category)
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
pass
list_display — List Columns
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ["id", "name", "price", "stock", "category", "is_active", "created_at"]
list_display_links = ["id", "name"]
list_editable = ["is_active", "stock"]
list_filter = ["is_active", "category", "created_at"]
search_fields = ["name", "description", "owner__email"]
ordering = ["-created_at"]
list_per_page = 25
def price_display(self, obj):
return f"${obj.price:,.2f}"
price_display.short_description = "Price"
price_display.admin_order_field = "price"
Detail Form Customization
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
fieldsets = [
("Basic Info", {
"fields": ["name", "description", "category"],
}),
("Price & Stock", {
"fields": ["price", "stock"],
"classes": ["wide"],
}),
("Settings", {
"fields": ["is_active", "owner"],
"classes": ["collapse"],
}),
]
readonly_fields = ["created_at", "updated_at"]
autocomplete_fields = ["category", "owner"]
raw_id_fields = ["owner"]
Custom Actions
from django.contrib import admin, messages
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ["name", "is_active", "stock"]
actions = ["activate_products", "deactivate_products", "restock"]
@admin.action(description="Activate selected products")
def activate_products(self, request, queryset):
count = queryset.update(is_active=True)
self.message_user(request, f"{count} products activated")
@admin.action(description="Deactivate selected products")
def deactivate_products(self, request, queryset):
count = queryset.update(is_active=False)
self.message_user(request, f"{count} products deactivated", messages.WARNING)
@admin.action(description="Reset stock to 100")
def restock(self, request, queryset):
count = queryset.update(stock=100)
self.message_user(request, f"{count} products restocked to 100")
Inline — Edit Related Models Together
from django.contrib import admin
from .models import Product, Review
class ReviewInline(admin.TabularInline):
model = Review
fields = ["author", "rating", "comment"]
readonly_fields = ["created_at"]
extra = 0
max_num = 10
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ["name", "price", "review_count"]
inlines = [ReviewInline]
def review_count(self, obj):
return obj.reviews.count()
review_count.short_description = "Reviews"
def get_queryset(self, request):
return super().get_queryset(request).prefetch_related("reviews")
Permission Control
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
def has_add_permission(self, request):
return request.user.is_superuser
def has_change_permission(self, request, obj=None):
if obj and obj.owner == request.user:
return True
return request.user.is_staff
def has_delete_permission(self, request, obj=None):
return request.user.is_superuser
def get_queryset(self, request):
qs = super().get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(owner=request.user)
Admin Site Customization
admin.site.site_header = "My Shop Admin"
admin.site.site_title = "My Shop Admin"
admin.site.index_title = "Dashboard"
Summary
| Feature | Attribute/Method |
|---|---|
| List columns | list_display |
| Inline edit | list_editable |
| Sidebar filter | list_filter |
| Search | search_fields |
| Form groups | fieldsets |
| Read-only | readonly_fields |
| Custom actions | actions + @admin.action |
| Related inline | TabularInline / StackedInline |
| Permission control | has_*_permission() |
Django Admin is best suited for internal tools and content management systems.