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

如何在Django REST Framework中实现规范的自定义用户头像权限?

自定义用户头像访问权限的实现方案

问题背景

我需要编写一个自定义权限来限制用户头像的访问权限。现有模型、序列化器和视图代码如下:

Member 模型与 MemberDisplayPicture 模型

# imports
class Member(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)

    user = models.OneToOneField(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE, unique=True)
    sex = models.IntegerField(choices=[(0, 'Unknown'), (1, 'Male'), (2, 'Female'), (9, 'Not Applicable')])
    date_of_birth = models.DateField()

    bio = models.TextField(null=True)

    def __str__(self) -> str:
        return self.user.username


def _display_picture_upload_path(instance, filename: str):
    return f'members/display/{instance.member}.jpg'


class MemberDisplayPicture(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)

    image = models.ImageField(upload_to=_display_picture_upload_path)
    member = models.OneToOneField(to=Member, on_delete=models.CASCADE, related_name='display_picture', unique=True)

MemberDisplayPicture 序列化器

class MemberDisplayPictureSerializer(serializers.ModelSerializer):
    class Meta:
        model = MemberDisplayPicture
        fields = ['id', 'image']

    def create(self, validated_data):
        member_id = self.context['member_id']
        instance = MemberDisplayPicture.objects.create(member_id=member_id, **validated_data)
        return instance

视图实现

class MemberDisplayPictureAPI(RetrieveModelMixin, CreateModelMixin, DestroyModelMixin, GenericAPIView):
    http_method_names = ['get', 'post', 'delete']

    serializer_class = MemberDisplayPictureSerializer

    def get_member_id(self):
        member_id = self.kwargs['pk']
        return member_id

    def get_queryset(self):
        return MemberDisplayPicture.objects.filter(member_id=self.get_member_id())

    def get_object(self):
        queryset = self.filter_queryset(queryset=self.get_queryset())
        obj = get_object_or_404(queryset)
        self.check_object_permissions(self.request, obj)
        return obj

    def get_serializer_context(self):
        return {'member_id': self.get_member_id()}

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

权限需求

  • 仅允许已认证用户访问;
  • 允许任何人查询用户头像;
  • 登录用户仅能创建或删除自己的头像;
  • 超级用户可查询、创建或删除任意用户头像。

解决方案

1. 编写自定义权限类

在应用的 permissions.py 文件中创建自定义权限类,遵循DRF权限类规范:

from rest_framework import permissions
from .models import Member

class MemberDisplayPicturePermission(permissions.BasePermission):
    def has_permission(self, request, view):
        # 所有请求必须来自已认证用户
        if not request.user.is_authenticated:
            return False
        
        # GET请求直接放行,允许所有已认证用户查询
        if request.method == 'GET':
            return True
        
        # 超级用户拥有所有操作权限
        if request.user.is_superuser:
            return True
        
        # 非GET请求验证是否操作自己的Member记录
        member_id = view.kwargs.get('pk')
        try:
            member = Member.objects.get(id=member_id)
            return member.user == request.user
        except Member.DoesNotExist:
            return False

    def has_object_permission(self, request, view, obj):
        # GET请求允许访问
        if request.method == 'GET':
            return True
        
        # 超级用户拥有所有权限
        if request.user.is_superuser:
            return True
        
        # 普通用户仅能操作自己的头像
        return obj.member.user == request.user

2. 在视图中应用自定义权限

修改 MemberDisplayPictureAPI 类,添加权限类配置:

class MemberDisplayPictureAPI(RetrieveModelMixin, CreateModelMixin, DestroyModelMixin, GenericAPIView):
    http_method_names = ['get', 'post', 'delete']
    serializer_class = MemberDisplayPictureSerializer
    # 绑定自定义权限
    permission_classes = [MemberDisplayPicturePermission]

    # 其余原有代码保持不变
    def get_member_id(self):
        member_id = self.kwargs['pk']
        return member_id

    def get_queryset(self):
        return MemberDisplayPicture.objects.filter(member_id=self.get_member_id())

    def get_object(self):
        queryset = self.filter_queryset(queryset=self.get_queryset())
        obj = get_object_or_404(queryset)
        self.check_object_permissions(self.request, obj)
        return obj

    def get_serializer_context(self):
        return {'member_id': self.get_member_id()}

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

3. 权限逻辑说明

  • has_permission:处理全局权限校验,先确保请求来自已认证用户;对GET请求直接放行;超级用户拥有全量操作权限;普通用户的POST/DELETE请求需验证目标Member是否属于自己。
  • has_object_permission:处理对象级权限校验,GET请求直接放行;超级用户拥有全量权限;普通用户仅能访问/操作关联自己账号的头像对象。

该实现完全覆盖所有权限需求,且遵循DRF最佳实践,权限逻辑与业务代码分离,便于后续维护和扩展。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 02:18:17