浏览知识库目录

Python

管理后台定制

管理后台定制

Django Admin 适合内部人员管理数据,不是面向普通用户的前台页面。本篇将把默认后台改造成具备列表、搜索、筛选、批量操作和关联编辑能力的运营工具。


一、注册模型

from django.contrib import admin

from .models import Message


@admin.register(Message)
class MessageAdmin(admin.ModelAdmin):
    list_display = ("name", "owner", "status", "created_at")
    list_filter = ("status", "created_at")
    search_fields = ("name", "content", "owner__username")
    ordering = ("-created_at",)
    list_per_page = 30

list_display 中的方法和关联字段可能引发额外查询。数据量大时要观察后台列表的查询数量。


二、编辑页布局

@admin.register(Message)
class MessageAdmin(admin.ModelAdmin):
    fieldsets = (
        ("基本信息", {"fields": ("owner", "name", "content")}),
        ("发布设置", {"fields": ("status", "tags")}),
        ("时间信息", {
            "fields": ("created_at", "updated_at"),
            "classes": ("collapse",),
        }),
    )
    readonly_fields = ("created_at", "updated_at")
    filter_horizontal = ("tags",)

只读字段必须同时出现在 readonly_fields 中,否则不可编辑字段通常不会显示在表单里。


三、关联对象内联编辑

from .models import Attachment


class AttachmentInline(admin.TabularInline):
    model = Attachment
    extra = 0
    fields = ("file", "uploaded_at")
    readonly_fields = ("uploaded_at",)


@admin.register(Message)
class MessageAdmin(admin.ModelAdmin):
    inlines = [AttachmentInline]

TabularInline 更紧凑,StackedInline 更适合字段较多的子对象。


四、自定义列表字段

from django.contrib import admin


@admin.display(description="摘要", ordering="content")
def content_preview(self, obj):
    return obj.content[:30]

把它加入 list_display

list_display = ("name", "content_preview", "status", "created_at")

不要返回拼接的未转义 HTML。确实需要 HTML 时使用 format_html()


五、批量操作

@admin.action(description="将选中留言设为已发布")
def make_published(modeladmin, request, queryset):
    updated = queryset.update(status=Message.Status.PUBLISHED)
    modeladmin.message_user(request, f"已发布 {updated} 条留言。")


@admin.register(Message)
class MessageAdmin(admin.ModelAdmin):
    actions = [make_published]

queryset.update() 不会调用每个对象的 save(),也不会触发 pre_save/post_save 信号。如果业务依赖这些行为,应逐条处理或把规则提取到明确的服务函数中。


六、自动填写当前用户

def save_model(self, request, obj, form, change):
    if not change or obj.owner_id is None:
        obj.owner = request.user
    super().save_model(request, obj, form, change)

如果普通后台用户不能选择 owner,可通过 excludeget_readonly_fields() 或自定义表单限制。


七、后台权限

def get_queryset(self, request):
    queryset = super().get_queryset(request)
    if request.user.is_superuser:
        return queryset
    return queryset.filter(owner=request.user)


def has_delete_permission(self, request, obj=None):
    return request.user.is_superuser

还可重写 has_view_permission()has_add_permission()has_change_permission()。后台权限仍需配合用户组和最小权限原则。


八、查询优化

list_select_related = ("owner",)


def get_queryset(self, request):
    return super().get_queryset(request).prefetch_related("tags")

大数据量后台还应注意:

  • 搜索字段是否有合适索引。
  • date_hierarchy 和精确总数查询的成本。
  • 外键下拉框是否过大,可使用 autocomplete_fieldsraw_id_fields
autocomplete_fields = ("owner",)

被自动完成引用的模型后台必须配置 search_fields


九、后台品牌信息

admin.site.site_header = "留言平台管理后台"
admin.site.site_title = "留言平台"
admin.site.index_title = "数据管理"

深度改造后台界面前应评估成本。复杂业务流程、面向客户的功能或高度定制交互,通常更适合独立开发页面。


十、常见问题

  • 模型不显示:应用未注册、模型未注册或当前用户无权限。
  • 自定义字段报错:方法名没有加入 list_display,或签名不正确。
  • 批量操作没有触发业务逻辑:update() 绕过了模型 save() 和保存信号。
  • 外键下拉极慢:数据量大,应使用 autocomplete_fields
  • 普通员工看到全部数据:只配置了界面字段,未限制 get_queryset() 和权限。

十一、本篇检查清单

  • 能配置后台列表、搜索、筛选和排序。
  • 能组织编辑页和内联关联对象。
  • 能实现安全的批量操作。
  • 能控制不同后台用户的数据范围。
  • 能识别 Admin 适用范围,不把它当作全部前台页面。

上一篇:类视图与通用视图 | 下一篇:配置、中间件、信号与管理命令