Django REST Framework嵌套JSON同时创建用户与疾病实例问题排查
解决Django REST Framework创建用户时同步创建关联Disease实例的问题
问题现状
需要实现API在创建MyUser实例的同时,同步创建关联的Disease实例,但当前请求返回400错误,响应提示:
{ "user_disease": [ "This field is required." ] }
请求体中的disease字段数据无法被序列化器识别,且关联的疾病实例未被创建。
问题排查
序列化器字段名不匹配:
序列化器中定义的嵌套字段是user_disease,但请求体传递的是disease,导致序列化器无法找到对应数据,触发必填字段校验错误。create方法未正确处理关联数据:
当前create方法直接创建用户,未提取并处理疾病数据;注释代码不仅键名错误(用了disease而非user_disease),创建Disease的语法也有误,外键参数需以user=user形式传递。用户密码处理不当:
使用MyUser.objects.create(**validated_data)创建用户时,密码会以明文存储,无法正常验证身份,应使用create_user自动处理密码哈希。冗余方法无效:
SignUpAPIView继承自APIView,perform_create方法不会被框架调用,属于冗余代码。
解决方案
1. 修改序列化器(accounts/serializers.py)
调整字段名与请求体一致,正确实现嵌套数据的创建逻辑:
class DiseaseSerializer(serializers.ModelSerializer): class Meta: model = Disease fields = ['arrhythmia'] # 根据实际业务需求添加其他疾病字段 class MyUserSerializer(serializers.ModelSerializer): # 字段名与请求体的"disease"保持一致 disease = DiseaseSerializer(many=False, read_only=False) class Meta: model = MyUser # 明确指定业务所需字段,避免包含AbstractUser的敏感/不必要字段 fields = [ 'identifier', 'password', 'username', 'email', 'birth', 'gender', 'isForeigner', 'height', 'weight', 'pw_queiston_idx', 'pw_answer', 'disease' ] def create(self, validated_data): # 从验证数据中取出疾病相关数据 disease_data = validated_data.pop('disease') # 使用create_user创建用户,自动处理密码哈希 user = MyUser.objects.create_user(**validated_data) # 创建关联的Disease实例,绑定到当前用户 Disease.objects.create(user=user, **disease_data) return user
2. 修正视图(accounts/views.py)
删除无用的perform_create方法,保持代码简洁:
class SignUpAPIView(APIView): permission_classes = [AllowAny] def post(self, request): serializer = MyUserSerializer(data=request.data) if serializer.is_valid(): user = serializer.save() # 生成JWT Token对 token = TokenObtainPairSerializer.get_token(user) refresh_token = str(token) access_token = str(token.access_token) res = Response( { "user": serializer.data, "message": "sign_up success", "token": { "access": access_token, "refresh": refresh_token } }, status=status.HTTP_201_CREATED ) # 将Token存入HttpOnly Cookie res.set_cookie("access", access_token, httponly=True) res.set_cookie("refresh", refresh_token, httponly=True) return res else: print("is_valid() is false!") return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
关键修改说明
- 字段名对齐:序列化器嵌套字段名改为
disease,与请求体键匹配,解决必填字段报错问题。 - 密码安全:使用
create_user替代create,自动对密码哈希处理,保证账号安全。 - 关联创建:先创建用户实例,再以用户为外键创建
Disease,确保关联关系正确建立。 - 字段明确:避免使用
fields = '__all__',明确指定业务字段,减少不必要的字段暴露或错误。
内容的提问来源于stack exchange,提问作者LDG_idiot
相关产品推荐
相关产品推荐

