Admin 与认证
这一课你将能做到
- 用 Django 自带的 Admin 零代码管理数据,并自定义列表显示、搜索、过滤和操作
- 理解 Django 内置 User 模型的字段和扩展方式(AbstractUser vs AbstractBaseUser)
- 用
django.contrib.auth 完成登录、登出、权限检查和组管理
为什么要学
Django Admin 是框架最惊艳的功能之一——你定义了模型,自动就能在后台增删改查,不需要额外写一行前端代码。内部管理系统、CMS、内容运营后台,用 Admin 搭建只需几分钟。
和 Spring Boot 对比:Spring 没有内置管理后台,需要自写或用第三方(如 Spring Boot Admin 只监控 JVM)。Django Admin 直接管理业务数据,开箱即用。这是 Django 做内部系统的巨大优势。
先建立图像
管理员访问 /admin/
│
▼
Django Admin 入口
│
├─ 模型列表(自定义 admin.py 注册)
│ ├─ Article:列表、搜索、过滤、批量操作
│ └─ User:内置用户管理
│
└─ 认证与授权
├─ Users(用户)
├─ Groups(组/角色)
└─ Permissions(权限)
跟着做
步骤 1:开启 Admin 并创建超级用户
python manage.py createsuperuser
# 按提示输入用户名、邮箱、密码
访问 http://127.0.0.1:8000/admin/,用超级用户登录,默认能看到 Users 和 Groups。
步骤 2:注册自定义模型
blog/admin.py:
from django.contrib import admin
from .models import Article, Category, Tag
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ["name", "slug", "created_at"]
search_fields = ["name"]
prepopulated_fields = {"slug": ("name",)} # 根据 name 自动填充 slug
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
list_display = ["name"]
search_fields = ["name"]
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
list_display = ["title", "author", "category", "status", "views", "created_at", "is_hot"]
list_filter = ["status", "category", "created_at", "author"]
search_fields = ["title", "content"]
list_editable = ["status", "views"]
list_per_page = 20
date_hierarchy = "created_at"
autocomplete_fields = ["author", "category"] # 外键用搜索框代替下拉(大数据量必备)
filter_horizontal = ["tags"] # 多对多用双列选择器
actions = ["make_published", "reset_views"]
fieldsets = [
("基本信息", {"fields": ["title", "slug", "author", "category", "tags"]}),
("内容", {"fields": ["content"], "classes": ["collapse"]}),
("状态", {"fields": ["status", "views"], "classes": ["wide"]}),
]
@admin.action(description="设为已发布")
def make_published(self, request, queryset):
queryset.update(status="published")
self.message_user(request, f"已发布 {queryset.count()} 篇文章")
@admin.action(description="重置阅读数")
def reset_views(self, request, queryset):
from django.db.models import F
queryset.update(views=0)
@admin.display(description="热门", boolean=True)
def is_hot(self, obj):
return obj.views > 1000
步骤 3:自定义 Admin 外观
# blog/admin.py
admin.site.site_header = "我的博客管理后台"
admin.site.site_title = "管理后台"
admin.site.index_title = "欢迎"
步骤 4:权限系统——用户、组、权限
User(用户)←── 多对多 ──→ Group(组)
│ │
└─ 多对多 ──→ Permission(权限)
- 模型级:
blog.view_article、blog.change_article、blog.add_article、blog.delete_article
- 对象级(需要额外配置):Django-guardian 等第三方包
from django.contrib.auth.decorators import login_required, permission_required
@login_required
def create_article(request):
return HttpResponse("创建文章")
@permission_required("blog.change_article", raise_exception=True)
def edit_article(request, pk):
return HttpResponse(f"编辑文章 {pk}")
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.views.generic import UpdateView
class ArticleUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
model = Article
permission_required = "blog.change_article"
template_name = "blog/article_form.html"
步骤 5:扩展 User 模型
方案 A:继承 AbstractUser(推荐 90% 场景)
# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
avatar = models.ImageField(upload_to="avatars/", blank=True)
bio = models.TextField(blank=True)
github = models.URLField(blank=True)
class Meta:
verbose_name = "用户"
verbose_name_plural = "用户"
# settings.py
AUTH_USER_MODEL = "accounts.User"
# accounts/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
@admin.register(User)
class CustomUserAdmin(UserAdmin):
fieldsets = UserAdmin.fieldsets + (
("扩展信息", {"fields": ["avatar", "bio", "github"]}),
)
list_display = ["username", "email", "bio", "is_staff"]
方案 B:继承 AbstractBaseUser(完全自定义,只保留密码和最后登录时间)
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.db import models
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError("邮箱必填")
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password=None, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
return self.create_user(email, password, **extra_fields)
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
nickname = models.CharField(max_length=50)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["nickname"]
objects = UserManager()
关键区别:AbstractUser 保留 username、first_name、last_name、email 等字段,扩展最方便。AbstractBaseUser 是裸机,全自己定义,适合手机号/邮箱登录的非标准场景。
步骤 6:内置认证视图
# urls.py
from django.contrib.auth import views as auth_views
urlpatterns = [
path("login/", auth_views.LoginView.as_view(template_name="login.html"), name="login"),
path("logout/", auth_views.LogoutView.as_view(next_page="/"), name="logout"),
path("password_change/", auth_views.PasswordChangeView.as_view(), name="password_change"),
path("password_reset/", auth_views.PasswordResetView.as_view(), name="password_reset"),
]
Django 内置了登录、登出、密码修改、密码重置全套视图,你只需要写模板。
讲清楚要点
- Admin 的本质是一个自动生成 CRUD 视图的框架:它在启动时扫描
admin.py 里注册的模型,动态生成列表页、编辑页、删除确认页。不是魔法,就是代码生成器。
@admin.register vs admin.site.register:@admin.register 是装饰器写法,更 Pythonic。两者等价。
- 超级用户 vs 普通用户 vs 组:超级用户(
is_superuser=True)有所有权限;组是把一批权限打包,批量赋给用户。Django 默认权限粒度到模型级别(CRUD),对象级需要第三方。
AUTH_USER_MODEL 必须尽早定:项目启动后改用户模型代价巨大(要重建迁移)。一建项目就要决定用内置 User 还是自定义。
- Admin 不是给终端用户用的:它是给内部运营、编辑、管理员用的。终端用户的界面要自己写模板和视图。
raise_exception=True:permission_required 默认未授权时重定向到登录页,加 raise_exception=True 直接返回 403。
你可能会懵的地方
-
为什么改了 User 模型要删库重来?
Django 的 auth_user 表被大量外键引用(如 Article.author),改字段类型或换模型会导致迁移链断裂。最佳实践:项目一启动就定义自定义 User,哪怕目前用不到额外字段。
-
is_staff、is_superuser、is_active 区别?
is_active:能否登录。is_staff:能否进 Admin 后台。is_superuser:是否拥有一切权限。可以 is_staff=True 但 is_superuser=False,做受限管理员。
-
对象级权限是什么?
模型级权限:你能「改所有文章」。对象级:你能「改自己的文章,不能改别人的」。Django 原生不支持,用 django-guardian 或自己写视图逻辑判断 request.user == article.author。
-
PermissionRequiredMixin 和 UserPassesTestMixin 区别?
前者检查权限字符串(如 "blog.change_article"),后者接受自定义 lambda/test 函数。后者更灵活,可以写任意业务规则。
-
Admin 的 raw_id_fields 和 autocomplete_fields 什么时候用?
外键关联的数据超过几百条时,默认下拉框会卡死。raw_id_fields 换成数字输入框 + 放大镜搜索;autocomplete_fields 换成搜索下拉框(更友好)。
错法对照
| 错法 | 问题 | 正解 |
|---|
项目中期才改 AUTH_USER_MODEL | 迁移链断裂,数据丢失风险 | 第一步就定义自定义 User |
用 User.objects.create() 创建用户 | 密码明文存储 | User.objects.create_user() 或 set_password() |
@login_required 放在 CBV 前面 | 装饰器不生效 | CBV 用 LoginRequiredMixin,或 @method_decorator(login_required) |
| Admin 直接暴露给公网 | 安全隐患 | Admin 配独立域名/IP 白名单,或改 URL 前缀 |
list_display 里放方法名但不加 @admin.display | 列名显示方法名 | 加 @admin.display(description="xxx") |
卡点急救
- 访问 Admin 提示 "You don't have permission":用户
is_staff=False。Admin 只给 staff 用。
- 自定义 User 后 migration 报错:之前的迁移还引用旧的
auth.User。删库(开发环境)或手动修迁移文件里的 swappable_dependency。
- Admin 里 ForeignKey 下拉几千条导致页面卡死:改用
autocomplete_fields 或 raw_id_fields。
面试常问
-
Django 的权限系统粒度到哪个层面?如何实现对象级权限?
原生到模型级(CRUD 四权限)。对象级需要第三方 django-guardian 或自己在视图里检查所有权。guardian 原理是给每个用户-对象组合分配权限,存额外一张表。
-
AbstractUser 和 AbstractBaseUser 怎么选?
保留 Django 默认用户字段(username、email、first_name 等)时选 AbstractUser。需要完全自定义(如只用手机号/邮箱当用户名、不要 first_name)选 AbstractBaseUser。90% 场景 AbstractUser 够用。
-
如何限制 Admin 只显示当前用户的数据?
重写 ModelAdmin.get_queryset():
def get_queryset(self, request):
qs = super().get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(author=request.user)
自测三问
is_staff、is_superuser、is_active 分别控制什么?
- 为什么推荐项目一开始就定义自定义 User 模型?
- Admin 的
autocomplete_fields 解决的是什么性能问题?
小结一句话
Django Admin 是零代码生成管理后台的利器,内置认证系统覆盖登录/权限/组;理解 User 模型扩展和权限粒度,是 Django 项目安全管理的基石。
延伸