如何在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
相关产品推荐
相关产品推荐

