You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django REST Framework多用户权限与自定义用户模型技术问询

问题解答

1. 是否需要创建自定义用户模型来实现上述需求?

是的,非常建议创建自定义用户模型。虽然你可以通过给默认auth.User添加分组(Group)或者额外关联模型(比如当前的Company/Employee与User关联)来区分角色,但自定义用户模型是Django官方推荐的最佳实践,原因如下:

  • 后期如果需要扩展用户字段(比如添加手机号、角色标识),自定义模型可以避免重构数据库的麻烦;
  • 把角色直接存在用户模型里,权限判断会更直接,减少关联查询;
  • 统一用户逻辑,避免多个模型关联带来的复杂度。

当然,如果项目已经处于中期不想重构默认用户模型,也可以通过OneToOneField给auth.User关联角色信息,但长远来看自定义模型更灵活。

2. 若需要,如何创建该自定义用户模型?

咱们一步步来实现:

第一步:定义自定义用户模型

在models.py中创建继承自AbstractUser的模型,添加角色字段:

from django.db import models
from django.contrib.auth.models import AbstractUser

class CustomUser(AbstractUser):
    ROLE_CHOICES = (
        ('admin', 'Admin'),
        ('company', '企业用户'),
        ('employee', '员工用户'),
    )
    role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='employee')

    def __str__(self):
        return self.username

第二步:配置settings.py

告诉Django使用你的自定义用户模型,在settings.py末尾添加:

AUTH_USER_MODEL = 'restcompanies.CustomUser'

第三步:更新关联模型

把原来Company、Employee里的ForeignKey('auth.User')替换为关联CustomUser:

# Company模型示例
class Company(models.Model):
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    empresa_nome = models.CharField(max_length=100, default='')
    razao_social = models.CharField(max_length=100, default='')
    cnpj = models.CharField(max_length=18, default='')
    
    def __str__(self):
        return self.empresa_nome

# Employee模型同理
class Employee(models.Model):
    empresa = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='employees')
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    phone = models.CharField(max_length=20)
    
    def __str__(self):
        return self.phone

第四步:迁移数据库

  • 新项目直接执行:
python manage.py makemigrations
python manage.py migrate
  • 已有数据的项目需先备份数据,清空现有迁移文件(除__init__.py),再重新生成迁移。

第五步:在admin中注册自定义用户

在admin.py中注册,方便后台管理:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser

admin.site.register(CustomUser, UserAdmin)

3. 如何设置符合上述规则的权限?

我们需要基于Django REST Framework的自定义权限类实现,每个角色对应不同的权限逻辑:

第一步:创建权限类

在app中新建permissions.py:

from rest_framework import permissions

class IsAdmin(permissions.BasePermission):
    """管理员权限:允许所有操作"""
    def has_permission(self, request, view):
        return request.user.is_authenticated and request.user.role == 'admin'

class IsCompanyOwner(permissions.BasePermission):
    """企业用户权限:仅能操作自身旗下资源"""
    def has_permission(self, request, view):
        return request.user.is_authenticated and request.user.role == 'company'

    def has_object_permission(self, request, view, obj):
        # 判断对象是否属于当前企业用户
        if hasattr(obj, 'empresa'):
            return obj.empresa.user == request.user
        elif hasattr(obj, 'company'):
            return obj.company.user == request.user
        elif isinstance(obj, Company):
            return obj.user == request.user
        return False

class IsEmployeeOfCompany(permissions.BasePermission):
    """员工权限:仅能查看本企业的视频"""
    def has_permission(self, request, view):
        return request.user.is_authenticated and request.user.role == 'employee'

    def has_object_permission(self, request, view, obj):
        employee = request.user.employee_set.first()
        if not employee:
            return False
        # 验证视频/测试是否属于员工所在企业
        if isinstance(obj, Video):
            return obj.video.company == employee.empresa
        elif isinstance(obj, Test):
            return obj.company == employee.empresa
        return False

第二步:在视图集里应用权限

修改views.py中的各个ViewSet,添加permission_classes并过滤查询集:

from rest_framework import viewsets
from .models import CustomUser, Company, Employee, Test, Video
from .serializers import (
    UserSerializer, CompanySerializer, EmployeeSerializer,
    TestSerializer, VideoSerializer
)
from .permissions import IsAdmin, IsCompanyOwner, IsEmployeeOfCompany

class UserViewSet(viewsets.ModelViewSet):
    queryset = CustomUser.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAdmin]  # 仅管理员可管理用户

class CompanyViewSet(viewsets.ModelViewSet):
    serializer_class = CompanySerializer
    permission_classes = [IsAdmin | IsCompanyOwner]

    def get_queryset(self):
        user = self.request.user
        if user.role == 'company':
            return Company.objects.filter(user=user)
        return Company.objects.all()

class EmployeeViewSet(viewsets.ModelViewSet):
    serializer_class = EmployeeSerializer
    permission_classes = [IsAdmin | IsCompanyOwner]

    def get_queryset(self):
        user = self.request.user
        if user.role == 'company':
            return Employee.objects.filter(empresa__user=user)
        return Employee.objects.all()

class TestViewSet(viewsets.ModelViewSet):
    serializer_class = TestSerializer
    permission_classes = [IsAdmin | IsCompanyOwner | IsEmployeeOfCompany]

    def get_queryset(self):
        user = self.request.user
        if user.role == 'company':
            return Test.objects.filter(company__user=user)
        elif user.role == 'employee':
            employee = user.employee_set.first()
            return Test.objects.filter(company=employee.empresa) if employee else Test.objects.none()
        return Test.objects.all()

class VideoViewSet(viewsets.ModelViewSet):
    serializer_class = VideoSerializer
    permission_classes = [IsAdmin | IsCompanyOwner | IsEmployeeOfCompany]

    def get_queryset(self):
        user = self.request.user
        if user.role == 'company':
            return Video.objects.filter(video__company__user=user)
        elif user.role == 'employee':
            employee = user.employee_set.first()
            return Video.objects.filter(video__company=employee.empresa) if employee else Video.objects.none()
        return Video.objects.all()

4. 如何管理注册表单、登录功能,以及管控不同用户可访问的路由?

注册表单与逻辑

自定义DRF的APIView处理注册,根据角色创建对应关联模型:

# views.py中添加注册视图
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .serializers import CustomUserSerializer, CompanySerializer, EmployeeSerializer

class RegisterView(APIView):
    def post(self, request):
        user_data = request.data.copy()
        role = user_data.get('role', 'employee')
        user_serializer = CustomUserSerializer(data=user_data)
        
        if user_serializer.is_valid():
            user = user_serializer.save()
            # 根据角色创建关联模型
            if role == 'company':
                company_data = request.data.get('company', {})
                company_data['user'] = user.id
                company_serializer = CompanySerializer(data=company_data)
                if company_serializer.is_valid():
                    company_serializer.save()
            elif role == 'employee':
                employee_data = request.data.get('employee', {})
                employee_data['user'] = user.id
                employee_serializer = EmployeeSerializer(data=employee_data)
                if employee_serializer.is_valid():
                    employee_serializer.save()
            return Response(user_serializer.data, status=status.HTTP_201_CREATED)
        return Response(user_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

在urls.py中添加注册路由:

from django.conf.urls import url
from .views import RegisterView

urlpatterns = [
    # 其他路由...
    url(r'^register/$', RegisterView.as_view(), name='register'),
]

登录功能

使用DRF的TokenAuthentication实现:

  1. 配置settings.py:
INSTALLED_APPS = [
    # ...
    'rest_framework.authtoken',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ],
}
  1. 迁移生成token表:
python manage.py migrate
  1. 添加登录路由:
from rest_framework.authtoken.views import obtain_auth_token

urlpatterns = [
    # 其他路由...
    url(r'^login/$', obtain_auth_token, name='login'),
]

用户POST用户名和密码到/login/即可获取token,后续请求在Headers中携带Authorization: Token <your-token>即可认证。

路由管控

主要通过视图集的permission_classes和get_queryset实现精准控制:

  • 管理员可访问所有路由;
  • 企业用户仅能访问自身旗下的公司、员工、测试和视频;
  • 员工仅能访问所在企业的测试和视频。

无需额外路由层面的复杂配置,权限类已经能满足需求。


内容的提问来源于stack exchange,提问作者AND4011002849

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:52:52