DRF Serializer处理ManyToMany字段时无法接收请求传值的问题咨询
根因分析
- 你的序列化器中
users字段使用了many=True的嵌套UserInfoSerializer,DRF默认期望该字段接收包含id字段的字典列表格式(如[{"id":11},{"id":1}]),但你实际传入的是逗号拼接的字符串"11,1",格式不匹配导致校验失败,序列化器判定users字段未传,所以抛出required错误。 - 你将
read_only设为True后,序列化器会直接忽略该字段的输入校验,也不会将其放入validated_data中,因此就算你写了自定义create方法也拿不到用户id,无法完成多对多关联。 - 你自定义的
create方法逻辑本身没问题,但因为校验阶段就失败了,请求根本不会走到create方法执行。
可行解决方案
你可以根据实际场景选以下任意一种方案:
方案1:使用PrimaryKeyRelatedField(最推荐,适配传id列表的场景)
如果你只需要传入用户id、不需要嵌套创建用户,直接用PrimaryKeyRelatedField替代嵌套序列化器即可,DRF会自动处理校验和关联:
class ChatRoomSerializer(serializers.ModelSerializer): # 接收用户id列表,自动校验id是否存在 users = serializers.PrimaryKeyRelatedField( many=True, queryset=CustomUser.objects.all() ) class Meta: model = ChatRoom fields = ['name', 'users']
此时请求体需要调整为数组格式的id:
{ "name": "benny", "users": [11, 1] }
不需要自定义create方法,DRF会自动完成多对多字段的关联。如果需要同时返回用户的其他信息,可以额外新增一个只读的嵌套序列化器字段:
class ChatRoomSerializer(serializers.ModelSerializer): users = serializers.PrimaryKeyRelatedField( many=True, queryset=CustomUser.objects.all(), write_only=True ) # 只读字段,返回用户信息 user_list = UserInfoSerializer(many=True, read_only=True, source="users") class Meta: model = ChatRoom fields = ['name', 'users', 'user_list']
方案2:保留逗号分隔字符串的传参格式,自定义预处理逻辑
如果前端没法调整传参格式,必须传逗号拼接的字符串,可以在序列化器中先做格式转换再校验:
class ChatRoomSerializer(serializers.ModelSerializer): users = serializers.ListField( child=serializers.IntegerField(), write_only=True ) user_list = UserInfoSerializer(many=True, read_only=True, source="users") def to_internal_value(self, data): # 把逗号拼接的字符串转为id列表 if isinstance(data.get("users"), str): data["users"] = [ int(id.strip()) for id in data["users"].split(",") if id.strip().isdigit() ] return super().to_internal_value(data) def create(self, validated_data): users = validated_data.pop("users") chat = ChatRoom.objects.create(**validated_data) chat.users.add(*users) return chat class Meta: model = ChatRoom fields = ['name', 'users', 'user_list']
该方案不需要调整前端传参,原来的users: "11,1"格式可以直接使用。
方案3:保留嵌套序列化器写法,调整传参格式
如果你坚持要使用嵌套的UserInfoSerializer接收输入,只需要把请求体的users调整为嵌套字典列表格式即可:
{ "name": "benny", "users": [{"id": 11}, {"id": 1}] }
此时你之前写的自定义create方法可以正常执行,不需要修改序列化器代码。
内容的提问来源于stack exchange,提问作者Sobbaka
相关产品推荐
相关产品推荐

