Django Admin后台管理详解

课程目标

  • 理解Django Admin的功能和作用
  • 掌握Admin界面的自定义配置
  • 学会创建和管理管理员用户
  • 了解Admin高级功能和安全设置

Django Admin概述

Django Admin是Django框架内置的强大后台管理界面,它允许开发者快速创建一个功能完善的管理后台,用于管理应用程序的数据。Admin基于应用程序的模型自动生成管理界面,大大减少了开发时间。

Admin的主要功能:

  • 自动生成CRUD操作界面
  • 用户认证和权限管理
  • 数据导入导出
  • 搜索和过滤功能
  • 自定义界面和行为

Admin基本配置

创建超级用户

python manage.py createsuperuser

按照提示输入用户名、邮箱和密码即可创建超级用户。

注册模型到Admin

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

# 最简单的注册方式
admin.site.register(Article)

# 自定义Admin类
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ['name', 'slug', 'created_at']
    prepopulated_fields = {'slug': ('name',)}
    search_fields = ['name']
    list_filter = ['created_at']

# 也可以这样注册
class TagAdmin(admin.ModelAdmin):
    list_display = ['name', 'usage_count']
    search_fields = ['name']

admin.site.register(Tag, TagAdmin)

Admin界面自定义

列表页面自定义

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    # 显示的字段
    list_display = [
        'title', 
        'author', 
        'category', 
        'status', 
        'created_at', 
        'view_count'
    ]
    
    # 每页显示数量
    list_per_page = 20
    
    # 可点击排序的字段
    list_display_links = ['title']
    
    # 右侧过滤器
    list_filter = [
        'status', 
        'category', 
        'created_at', 
        'author'
    ]
    
    # 搜索字段
    search_fields = [
        'title', 
        'content', 
        'author__username'
    ]
    
    # 日期层次导航
    date_hierarchy = 'created_at'
    
    # 字段排序
    ordering = ['-created_at']

表单页面自定义

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    # 字段分组
    fieldsets = (
        ('基本信息', {
            'fields': ('title', 'author', 'category')
        }),
        ('内容', {
            'fields': ('content', 'summary'),
            'classes': ('wide',)
        }),
        ('发布设置', {
            'fields': ('status', 'tags', 'publish_date'),
            'classes': ('collapse',)  # 可折叠
        }),
        ('统计信息', {
            'fields': ('view_count', 'like_count'),
            'classes': ('collapse',)
        })
    )
    
    # 显示的字段
    fields = ['title', 'content', 'author', 'category', 'status']
    
    # 多对多字段的显示方式
    filter_horizontal = ['tags']  # 水平显示多对多字段
    # 或者
    # filter_vertical = ['tags']  # 垂直显示多对多字段

表单布局自定义

class ArticleAdmin(admin.ModelAdmin):
    # 使用TabularInline或StackedInline
    class CategoryInline(admin.TabularInline):  # 水平显示
        model = ArticleCategory
        extra = 1
    
    class CommentInline(admin.StackedInline):   # 垂直显示
        model = Comment
        extra = 0
        readonly_fields = ['content', 'created_at']
    
    inlines = [CategoryInline, CommentInline]
    
    # 只读字段
    readonly_fields = ['created_at', 'updated_at', 'view_count']
    
    # 自定义动作
    actions = ['make_published', 'make_draft', 'export_as_pdf']
    
    def make_published(self, request, queryset):
        """批量发布文章"""
        updated = queryset.update(status='published')
        self.message_user(
            request, 
            f'成功将 {updated} 篇文章设置为发布状态'
        )
    
    make_published.short_description = "批量发布选中的文章"
    
    def make_draft(self, request, queryset):
        """批量设为草稿"""
        updated = queryset.update(status='draft')
        self.message_user(
            request, 
            f'成功将 {updated} 篇文章设置为草稿状态'
        )
    
    make_draft.short_description = "批量设为草稿"

高级Admin功能

自定义Admin类

from django.contrib import admin
from django.utils.html import format_html
from django.urls import reverse
from django.utils.safestring import mark_safe

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = [
        'title', 
        'author', 
        'status', 
        'created_at', 
        'view_count', 
        'custom_actions'
    ]
    
    list_filter = ['status', 'category', 'created_at']
    search_fields = ['title', 'content', 'author__username']
    date_hierarchy = 'created_at'
    
    # 自定义列表操作列
    def custom_actions(self, obj):
        """自定义操作按钮"""
        edit_url = reverse('admin:myapp_article_change', args=[obj.pk])
        view_url = reverse('article_detail', args=[obj.pk])
        
        return format_html(
            '<a class="button" href="{}" target="_blank">编辑</a> '
            '<a class="button" href="{}" target="_blank">查看</a>',
            edit_url,
            view_url
        )
    
    custom_actions.short_description = '操作'
    
    # 自定义查询集
    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(author=request.user)
    
    # 自定义保存行为
    def save_model(self, request, obj, form, change):
        if not change:  # 如果是新建
            obj.author = request.user
        super().save_model(request, obj, form, change)
    
    # 自定义删除行为
    def delete_model(self, request, obj):
        # 可以在这里添加自定义删除逻辑
        super().delete_model(request, obj)

Admin权限控制

class ArticleAdmin(admin.ModelAdmin):
    def has_add_permission(self, request):
        """控制添加权限"""
        # 只有超级用户和编辑可以添加
        return request.user.is_superuser or request.user.groups.filter(name='Editor').exists()
    
    def has_change_permission(self, request, obj=None):
        """控制修改权限"""
        # 超级用户可以修改所有,普通用户只能修改自己的
        if request.user.is_superuser:
            return True
        if obj is None:
            return True
        return obj.author == request.user
    
    def has_delete_permission(self, request, obj=None):
        """控制删除权限"""
        if request.user.is_superuser:
            return True
        if obj is None:
            return False
        return obj.author == request.user and obj.status == 'draft'
    
    def get_readonly_fields(self, request, obj=None):
        """动态设置只读字段"""
        readonly_fields = []
        if not request.user.is_superuser:
            readonly_fields.extend(['author', 'created_at'])
        if obj and obj.status == 'published':
            readonly_fields.append('author')  # 已发布的文章不能更改作者
        return readonly_fields

Admin样式和主题

自定义Admin外观

# admin.py
from django.contrib import admin
from django.utils.html import format_html

# 自定义站点头部
admin.site.site_header = '我的网站管理后台'
admin.site.site_title = '管理后台'
admin.site.index_title = '欢迎来到我的网站管理后台'

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = [
        'title', 
        'formatted_status', 
        'author', 
        'created_at'
    ]
    
    def formatted_status(self, obj):
        """美化状态显示"""
        status_colors = {
            'draft': 'orange',
            'published': 'green',
            'archived': 'gray'
        }
        color = status_colors.get(obj.status, 'black')
        return format_html(
            '<span style="color: {}; font-weight: bold;">{}</span>',
            color,
            obj.get_status_display()
        )
    
    formatted_status.short_description = '状态'
    formatted_status.admin_order_field = 'status'

自定义CSS样式

# admin.py
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    class Media:
        css = {
            'all': ('/static/admin/css/custom_admin.css',)
        }
        js = (
            '/static/admin/js/custom_admin.js',
        )
    
    list_display = ['title', 'author', 'status', 'created_at']
/* static/admin/css/custom_admin.css */
.module h2 {
    background-color: #417690;
    color: white;
    padding: 10px;
}

.field-status {
    font-weight: bold;
}

.draft {
    color: orange;
}

.published {
    color: green;
}

.archived {
    color: gray;
}

Admin高级技巧

批量操作

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    actions = [
        'publish_selected', 
        'unpublish_selected', 
        'export_selected'
    ]
    
    def publish_selected(self, request, queryset):
        """发布选中的文章"""
        updated = queryset.update(status='published')
        self.message_user(
            request,
            f'成功发布了 {updated} 篇文章。',
            messages.SUCCESS
        )
    
    publish_selected.short_description = "发布选中的文章"
    
    def unpublish_selected(self, request, queryset):
        """取消发布选中的文章"""
        updated = queryset.update(status='draft')
        self.message_user(
            request,
            f'成功取消发布 {updated} 篇文章。',
            messages.INFO
        )
    
    unpublish_selected.short_description = "取消发布选中的文章"
    
    def export_selected(self, request, queryset):
        """导出选中的文章"""
        import csv
        from django.http import HttpResponse
        
        response = HttpResponse(content_type='text/csv')
        response['Content-Disposition'] = 'attachment; filename="articles.csv"'
        
        writer = csv.writer(response)
        writer.writerow(['标题', '作者', '内容', '状态', '创建时间'])
        
        for article in queryset:
            writer.writerow([
                article.title,
                article.author.username,
                article.content[:100],  # 只导出前100个字符
                article.status,
                article.created_at
            ])
        
        return response
    
    export_selected.short_description = "导出选中的文章为CSV"

自定义Admin视图

from django.contrib import admin
from django.urls import path
from django.shortcuts import render
from django.http import JsonResponse
from .models import Article

class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'status', 'created_at']
    
    def get_urls(self):
        urls = super().get_urls()
        custom_urls = [
            path('stats/', self.admin_site.admin_view(self.stats_view), name='article-stats'),
        ]
        return custom_urls + urls
    
    def stats_view(self, request):
        """自定义统计视图"""
        articles_count = Article.objects.count()
        published_count = Article.objects.filter(status='published').count()
        draft_count = Article.objects.filter(status='draft').count()
        
        context = {
            'articles_count': articles_count,
            'published_count': published_count,
            'draft_count': draft_count,
            'title': '文章统计',
        }
        
        return render(request, 'admin/stats.html', context)

admin.site.register(Article, ArticleAdmin)

Admin安全设置

安全配置

# settings.py
# Admin安全设置
ADMIN_ENABLED = True  # 控制是否启用Admin

# 只允许特定IP访问Admin
ALLOWED_ADMIN_IPS = [
    '127.0.0.1',
    '192.168.1.1',
]

# 自定义Admin URL
ADMIN_URL = 'secure_admin_panel/'  # 不要使用默认的admin/

# 中间件中检查IP
"""
class AdminIPRestrictionMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        if request.path.startswith('/admin/'):
            ip = request.META.get('REMOTE_ADDR')
            if ip not in ALLOWED_ADMIN_IPS:
                return HttpResponseForbidden('Access denied')
        
        response = self.get_response(request)
        return response
"""

自定义Admin站点

# admin.py
from django.contrib.admin import AdminSite
from django.contrib.auth.models import User, Group

class MyAdminSite(AdminSite):
    site_header = '我的网站管理后台'
    site_title = '我的网站'
    index_title = '欢迎使用我的网站管理后台'
    
    def has_permission(self, request):
        """自定义权限检查"""
        return request.user.is_superuser

# 创建自定义Admin站点实例
my_admin_site = MyAdminSite(name='myadmin')

# 注册模型到自定义站点
my_admin_site.register(Article, ArticleAdmin)
my_admin_site.register(Category, CategoryAdmin)

# 在urls.py中使用
"""
from django.urls import path, include
from myapp.admin import my_admin_site

urlpatterns = [
    path('myadmin/', my_admin_site.urls),
    # 其他URL...
]
"""

课程总结

本节课我们深入学习了Django Admin后台管理系统的各个方面,包括基本配置、界面自定义、高级功能、权限控制和安全设置等。Django Admin是开发过程中非常有用的工具,可以大大提高数据管理的效率。通过合理的配置和自定义,我们可以创建出功能强大且用户友好的管理后台。