Django REST Framework无法添加对象:user字段类型错误求助
错误提示"Incorrect type. Expected pk value, received str."的核心原因是:序列化器处理user外键字段时,默认期望接收用户的主键(即id数值),但你传入的是字符串(比如用户的pseudo),导致验证不通过。同时你的PebblesSerializer的create方法参数错误,它应该接收validated_data而非request。
以下提供几种可行的修复方式,优先推荐方案三(利用认证用户自动填充,更安全):
方案一:前端传递用户ID,修正序列化器逻辑
如果需要前端指定用户(不推荐,因为已开启认证),让前端传用户的id值,同时修正序列化器的create方法:
class PebblesSerializer(serializers.ModelSerializer): class Meta: model = Pebbles fields = ["id","image","user"] # 修正create方法参数为validated_data def create(self, validated_data): return Pebbles.objects.create(**validated_data)
此时前端请求体示例:
{ "image": "xxx.jpg", "user": 1 // 这里填用户的id数值 }
方案二:自定义外键字段,支持通过pseudo关联用户
如果需要前端传用户的pseudo而非id,可以自定义外键字段,让序列化器自动根据pseudo查询用户:
class PebblesSerializer(serializers.ModelSerializer): # 自定义user字段,指定用pseudo匹配用户 user = serializers.SlugRelatedField( slug_field='pseudo', queryset=UserData.objects.all() ) class Meta: model = Pebbles fields = ["id","image","user"] def create(self, validated_data): return Pebbles.objects.create(**validated_data)
此时前端请求体示例:
{ "image": "xxx.jpg", "user": "test_pseudo" // 填用户的pseudo字符串 }
方案三:利用认证用户自动填充(推荐)
视图已经配置了IsAuthenticated权限,说明当前请求的用户已经登录,完全可以直接用request.user填充Pebbles的user字段,无需前端传递,既安全又避免错误:
方式1:重写视图的perform_create方法
修改PebblesView,去掉自定义的create方法,改用框架推荐的perform_create:
class PebblesView(generics.ListCreateAPIView): permission_classes = [IsAuthenticated] authentication_classes = [JWTAuthentication] serializer_class = PebblesSerializer queryset = Pebbles.objects.all() # 直接定义查询集,无需重写get_queryset # 重写该方法,自动传递当前登录用户 def perform_create(self, serializer): serializer.save(user=self.request.user)
同时修改PebblesSerializer,设置user为只读字段,避免前端传递:
class PebblesSerializer(serializers.ModelSerializer): class Meta: model = Pebbles fields = ["id","image","user"] read_only_fields = ["user"] # 标记为只读,前端无需传该字段
此时前端请求体只需传:
{ "image": "xxx.jpg" }
方式2:在序列化器的create方法中获取当前用户
也可以通过序列化器的context拿到请求对象,进而获取当前用户:
class PebblesSerializer(serializers.ModelSerializer): class Meta: model = Pebbles fields = ["id","image","user"] read_only_fields = ["user"] def create(self, validated_data): # 从context中取出request,拿到当前登录用户 user = self.context['request'].user return Pebbles.objects.create(user=user, **validated_data)
这种方式下视图无需额外修改,框架会自动调用序列化器的create方法。
你在Django Shell中能创建成功,是因为直接通过UserData.objects.get(pseudo=xxx)拿到了用户对象;但序列化器默认的外键字段只接受主键值,所以前端传字符串会触发验证错误。优先用方案三,既符合认证逻辑,又能避免前端传参错误。
内容的提问来源于stack exchange,提问作者Alves Gustavo

