如何在Django REST API的Serializer中填充creator_id并用于权限控制?
解决Post请求自动填充Creator及权限验证失效问题
问题背景
需要在Post请求中自动填充creator_id,并用于权限验证。此前在views.py中实现了creator字段的ID填充,数据库中creator列已成功写入ID(如"creator": "2"),但权限验证失效,无法删除自己创建的笔记,因此需要将填充逻辑移至serializers.py中实现自动填充。
问题根源
- 序列化配置错误:
serializers.py中creator字段设置了source="creator.id",但Model里的creator是CharField(存储字符串ID),字符串类型没有id属性,导致序列化逻辑异常,同时权限验证中obj.creator.id的写法会直接报错。 - 视图层修改request.data不规范:直接在
views.py中修改request.data的方式不符合DRF最佳实践,容易引发序列化验证冲突。
修正方案
方案一:将Creator改为外键关联User(推荐,符合数据库设计规范)
1. 修改models.py
将creator字段从CharField改为关联User模型的外键:
from django.contrib.auth.models import User import uuid class Note(models.Model): class NoteTypes(models.TextChoices): POST = "Post" IDEA = "Idea" category = models.CharField( max_length=4, choices=NoteTypes.choices, default=NoteTypes.POST ) # 关联User模型,on_delete根据业务需求选择CASCADE/PROTECT等 creator = models.ForeignKey(User, on_delete=models.CASCADE, related_name="notes") content = models.TextField(max_length=500) created_date = models.DateTimeField(auto_now_add=True) updated_date = models.DateTimeField(auto_now=True) uuid = models.UUIDField(default=uuid.uuid4, blank=True, primary_key=True) def __str__(self): return self.content class Meta: ordering = ["created_date"]
2. 修改serializers.py,实现自动填充
重写create方法,从上下文中获取当前用户并填充到creator字段:
from rest_framework import serializers from .models import Note class NoteSerializer(serializers.ModelSerializer): # 返回用户ID,若需返回用户名可改用serializers.StringRelatedField() creator = serializers.ReadOnlyField(source="creator.id") class Meta: model = Note fields = ["uuid", "category", "creator", "content", "created_date", "updated_date"] def create(self, validated_data): # 从上下文获取当前请求用户 user = self.context['request'].user validated_data['creator'] = user return super().create(validated_data)
3. 修改views.py,移除手动修改逻辑
初始化序列化器时传入context传递request对象,并添加权限类:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .models import Note from .serializers import NoteSerializer from .permissions import IsCreatorOrReadOnly class NotesList(APIView): permission_classes = [IsCreatorOrReadOnly] def get(self, request): notes = Note.objects.all() # 传递context给序列化器 serializer = NoteSerializer(notes, many=True, context={'request': request}) return Response(serializer.data) def post(self, request, format=None): serializer = NoteSerializer(data=request.data, context={'request': request}) # 移除手动修改request.data的代码 if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
4. 修改permissions.py,修正权限验证逻辑
直接比较用户对象即可:
from rest_framework import permissions class IsCreatorOrReadOnly(permissions.BasePermission): def has_object_permission(self, request, view, obj): # 安全方法(GET/HEAD/OPTIONS)允许所有访问 if request.method in permissions.SAFE_METHODS: return True # 验证当前用户是否为笔记创建者 return obj.creator == request.user
方案二:保留Creator为CharField(适合无需关联用户模型的场景)
1. 修改serializers.py
from rest_framework import serializers from .models import Note class NoteSerializer(serializers.ModelSerializer): creator = serializers.ReadOnlyField() class Meta: model = Note fields = ["uuid", "category", "creator", "content", "created_date", "updated_date"] def create(self, validated_data): # 将当前用户ID转为字符串存入creator字段 user_id = str(self.context['request'].user.id) validated_data['creator'] = user_id return super().create(validated_data)
2. 修改views.py
同方案一,移除手动修改request.data的代码,并传递context。
3. 修改permissions.py
比较字符串形式的用户ID:
from rest_framework import permissions class IsCreatorOrReadOnly(permissions.BasePermission): def has_object_permission(self, request, view, obj): if request.method in permissions.SAFE_METHODS: return True # 比较字符串类型的用户ID return obj.creator == str(request.user.id)
验证
- 执行数据库迁移(方案一需要):
python manage.py makemigrations及python manage.py migrate - 发送Post请求创建笔记,数据库中creator字段会自动填充当前用户ID
- 尝试删除自己创建的笔记,权限验证会正常生效
内容的提问来源于stack exchange,提问作者nataku
相关产品推荐
相关产品推荐

