自定义用户模型场景下Django组与权限的高效管理方案问询
我的现有实现背景
我在Django项目里一直习惯用自定义用户模型,通过role字段来控制用户对特定路由的访问权限,代码大概是这样的:
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager from django.db import models from django.core.validators import FileExtensionValidator from enum import Enum # 先假设USER_ROLES是一个枚举类 class USER_ROLES(Enum): ADMIN = "admin" EDITOR = "editor" VIEWER = "viewer" class CustomUserManager(BaseUserManager): # 这里省略自定义管理器的实现 pass class User(AbstractBaseUser, PermissionsMixin): name = models.CharField("full name", max_length=255, blank=True) email = models.EmailField("email address", unique=True) createdAt = models.DateField(auto_now_add=True) photo = models.ImageField( "profile photo", blank=True, null=True, validators=[ FileExtensionValidator(allowed_extensions=["jpg", "png", "jpeg"]) ], ) role = models.CharField( max_length=50, choices=[(role.value, role.name) for role in USER_ROLES], ) is_active = models.BooleanField(default=True) USERNAME_FIELD = "email" objects: CustomUserManager = CustomUserManager() def has_perm(self, perm, obj=None): if self.role == USER_ROLES.ADMIN.value: return True return super().has_perm(perm, obj) def has_module_perms(self, app_label): if self.role == USER_ROLES.ADMIN.value: return True return super().has_module_perms(app_label) @property def is_staff(self): return self.role == USER_ROLES.ADMIN.value @property def is_superuser(self): return self.role == USER_ROLES.ADMIN.value def __str__(self) -> str: return f"{self.pk}-{self.email}"
现在我意识到为了灵活性,最好切换到多角色模式,打算用Django内置的Group和Permission模型来实现。但我有点困惑:怎么在视图/API路由里处理用户的组和权限?如果之后需要同步组和权限的变更,该怎么做?有没有不依赖Django内置Admin面板的高效方案?
我之前想过在创建用户的视图里每次用Group.get_or_create,再写一个视图来给特定组添加或更新权限,然后用组权限来做授权,但不确定如果之后要修改用户组的话,这种方式是否靠谱。
我的建议方案
我之前也遇到过类似的需求,分享几个实用的思路,亲测在生产环境里挺好用的:
1. 提前初始化核心组与权限(避免重复创建)
不要每次创建用户都调用Group.get_or_create,而是用数据迁移来初始化系统核心组和对应权限,这样能保证组的唯一性,也不会在视图里做重复操作。
比如创建一个迁移文件:
from django.db import migrations from django.contrib.auth.models import Group, Permission def create_groups_and_permissions(apps, schema_editor): # 创建核心组 admin_group, _ = Group.objects.get_or_create(name="Admin") editor_group, _ = Group.objects.get_or_create(name="Editor") viewer_group, _ = Group.objects.get_or_create(name="Viewer") # 给组分配权限(比如给Admin组添加所有权限) all_permissions = Permission.objects.all() admin_group.permissions.add(*all_permissions) # 给Editor组添加特定模型的权限,比如文章的增改权限 article_perms = Permission.objects.filter(codename__in=["add_article", "change_article"]) editor_group.permissions.add(*article_perms) # Viewer组只给查看权限 viewer_perms = Permission.objects.filter(codename__in=["view_article"]) viewer_group.permissions.add(*viewer_perms) class Migration(migrations.Migration): dependencies = [ # 依赖你的用户模型所在app的迁移 ('your_app', '0001_initial'), ] operations = [ migrations.RunPython(create_groups_and_permissions), ]
这样系统启动后就有了预设的组和权限,后续只需要维护迁移文件来更新组权限即可,比在视图里处理更可靠。
2. 视图/API中处理用户组的增删改
对于用户组的变更,你可以写专门的API视图(比如用Django REST Framework)来处理,核心是操作user.groups这个多对多关系:
# 示例:给用户添加组的API视图 from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .models import User from django.contrib.auth.models import Group class AssignUserGroupView(APIView): def post(self, request): user_id = request.data.get("user_id") group_name = request.data.get("group_name") try: user = User.objects.get(pk=user_id) group = Group.objects.get(name=group_name) # 替换用户所有组(如果是追加用user.groups.add(group)) user.groups.set([group]) user.save() return Response({"message": "用户组已更新"}, status=status.HTTP_200_OK) except User.DoesNotExist: return Response({"error": "用户不存在"}, status=status.HTTP_404_NOT_FOUND) except Group.DoesNotExist: return Response({"error": "组不存在"}, status=status.HTTP_404_NOT_FOUND)
如果需要支持多组,只需要把group_name改成group_names列表,然后循环添加或者用set()方法批量设置就行。
3. 权限校验的实现
切换到组权限后,你可以用Django内置的权限装饰器或者自定义权限类来做校验:
- 函数视图可以用
@permission_required装饰器,比如:from django.contrib.auth.decorators import permission_required @permission_required("your_app.view_article", raise_exception=True) def article_list(request): # 视图逻辑 pass - 类视图(DRF)可以用
permission_classes:from rest_framework.permissions import IsAuthenticated, DjangoModelPermissions from rest_framework.viewsets import ModelViewSet from .models import Article from .serializers import ArticleSerializer class ArticleViewSet(ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer permission_classes = [IsAuthenticated, DjangoModelPermissions]
DjangoModelPermissions会自动根据用户所在组的权限来控制对模型的增删改查操作,非常省心。
4. 权限变更的同步
如果之后需要批量更新组的权限,直接修改迁移文件或者写一个管理命令来执行,比如:
# 管理命令示例:更新Editor组的权限 from django.core.management.base import BaseCommand from django.contrib.auth.models import Group, Permission class Command(BaseCommand): help = "更新Editor组的权限" def handle(self, *args, **options): editor_group = Group.objects.get(name="Editor") # 移除旧权限 old_perms = Permission.objects.filter(codename="delete_article") editor_group.permissions.remove(*old_perms) # 添加新权限 new_perms = Permission.objects.filter(codename="publish_article") editor_group.permissions.add(*new_perms) self.stdout.write(self.style.SUCCESS("Editor组权限已更新"))
这样比手动在Admin里改更可控,也方便版本控制。
总结一下
- 用迁移文件初始化组和权限,避免重复创建
- 写专门的视图/API来处理用户组的分配与变更
- 利用Django内置的权限校验工具替代自己写的role判断
- 用管理命令来批量同步权限变更
这种方式既灵活又能保证系统的一致性,完全不需要依赖Admin面板就能搞定所有权限管理操作。
内容来源于stack exchange

