You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django骑手注册接口嵌套序列化数据未正确返回问题求助

Django骑手注册字段未正确保存问题排查与解决

我在Django应用中实现骑手注册功能时,遇到嵌套序列化数据未正确返回的问题:

请求数据:

{   
    "email": "faruqmohammad@gmail.com",
    "first_name": "Faruq",
    "last_name": "Mohahmmad",
    "phone_number": "08137021976",
    "password": "#FaruqMohammad1234",
    "confirm_password": "#FaruqMohammad1234",
    "vehicle_registration_number": "ABJ145",
    "min_capacity": 20,
    "max_capacity": 50,
    "fragile_item_allowed": true,
    "charge_per_mile": 1000
}

响应数据:

{
    "data": {
        "user": {
            "email": "mohammedfaruq@gmail.com",
            "first_name": "Mohammed",
            "last_name": "Faruq",
            "phone_number": "08137021976"
        },
        "is_available": true,
        "vehicle_type": "TWO_WHEELER",
        "vehicle_registration_number": "",
        "min_capacity": null,
        "max_capacity": null,
        "fragile_item_allowed": true,
        "ratings": null,
        "charge_per_mile": null
    },
    "message": "Rider registration successful"
}

可见,vehicle_registration_number、min_capacity等骑手专属字段未获取请求数据,而是使用了模型默认值。


相关代码

Rider模型

class Rider(models.Model):
    user = models.OneToOneField(
        CustomUser, on_delete=models.CASCADE, related_name="rider_profile"
    )
    vehicle_type = models.CharField(max_length=50, default="TWO_WHEELER")
    vehicle_registration_number = models.CharField(max_length=20, unique=True)
    is_available = models.BooleanField(default=True)
    min_capacity = models.PositiveIntegerField(null=True, blank=True)
    max_capacity = models.PositiveIntegerField(null=True, blank=True)
    fragile_item_allowed = models.BooleanField(default=True)
    charge_per_mile = models.DecimalField(
        max_digits=6, decimal_places=2, null=True, blank=True
    )
    current_latitude = models.DecimalField(
        max_digits=9, decimal_places=6, null=True, blank=True
    )
    current_longitude = models.DecimalField(
        max_digits=9, decimal_places=6, null=True, blank=True
    )
    ratings = models.DecimalField(max_digits=3, decimal_places=2, null=True, blank=True)

    def __str__(self):
        return self.user.email

    def update_location(self, latitude, longitude):
        self.current_latitude = latitude
        self.current_longitude = longitude
        self.save()

序列化器

class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=True)
    confirm_password = serializers.CharField(write_only=True, required=True)

    class Meta:
        model = CustomUser
        fields = [
            "email",
            "first_name",
            "last_name",
            "phone_number",
            "password",
            "confirm_password",
        ]

    def validate_password(self, value):
        if len(value) < 8:
            raise serializers.ValidationError(
                "Password must be at least 8 characters long."
            )
        if not any(char.isupper() for char in value):
            raise serializers.ValidationError(
                "Password must contain at least one uppercase character."
            )
        special_characters = "!@#$%^&*()-_=+[]{}|;:'\",.<>/? "
        if not any(char in special_characters for char in value):
            raise serializers.ValidationError(
                "Password must contain at least one special character."
            )
        if not any(char.isdigit() for char in value):
            raise serializers.ValidationError(
                "Password must contain at least one number."
            )
        return value

    def validate_email(self, value):
        if CustomUser.objects.filter(email=value).exists():
            raise serializers.ValidationError("This email is already in use.")
        return value

    def validate(self, data):
        password = data.get("password")
        confirm_password = data.get("confirm_password")
        if password != confirm_password:
            raise serializers.ValidationError("Passwords do not match.")
        return data

    def create(self, validated_data):
        validated_data.pop("confirm_password", None)
        password = validated_data.get("password")
        if password is None:
            raise serializers.ValidationError("Password cannot be empty.")
        self.validate_password(password)
        user = CustomUser.objects.create_user(**validated_data)
        user.set_password(password)
        user.save()
        return user


class CustomUserNestedSerializer(serializers.ModelSerializer):
    class Meta:
        model = CustomUser
        fields = (
            "email",
            "first_name",
            "last_name",
            "phone_number",
        )


class RiderSerializer(serializers.ModelSerializer):
    user = CustomUserNestedSerializer(read_only=True)

    class Meta:
        model = Rider
        fields = [
            "user",
            "is_available",
            "vehicle_type",
            "vehicle_registration_number",
            "min_capacity",
            "max_capacity",
            "fragile_item_allowed",
            "ratings",
            "charge_per_mile",
        ]

视图

class BaseUserRegistrationView(APIView):
    user_model = None
    serializer_class = None

    @transaction.atomic
    def post(self, request, *args, **kwargs):
        logger.debug(f"Request_data: {request.data}")
        user_data = request.data
        user_serializer = UserSerializer(data=user_data)

        if user_serializer.is_valid():
            user_data = user_serializer.validated_data
            if user_data:
                try:
                    with transaction.atomic():
                        user = user_serializer.save()
                        user_obj = self.user_model.objects.create(user=user)
                        user_obj_serializer = self.serializer_class(user_obj).data

                        if user_obj_serializer:
                            send_verification_email(user, "registration")
                            return Response(
                                {
                                    "data": user_obj_serializer,
                                    "message": f"{self.user_model.__name__} registration successful",
                                },
                                status=status.HTTP_201_CREATED,
                            )
                        else:
                            return Response(
                                {
                                    "detail": "Validation error",
                                    "errors": user_serializer.errors,
                                },
                                status=status.HTTP_400_BAD_REQUEST,
                            )

                except IntegrityError as e:
                    logger.error(f"Integrity error: {e}")
                    return Response(
                        {
                            "detail": "Integrity error. Please ensure the data is unique."
                        },
                        status=status.HTTP_400_BAD_REQUEST,
                    )
                except Exception as e:
                    logger.error(f"An unexpected error occurred: {e}")
                    return Response(
                        {"detail": "An unexpected error occurred."},
                        status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    )
        else:
            return Response(
                {"detail": "Validation error", "errors": user_serializer.errors},
                status=status.HTTP_400_BAD_REQUEST,
            )


class RiderRegistrationView(BaseUserRegistrationView):
    user_model = Rider
    serializer_class = RiderSerializer

问题原因

核心问题在于创建Rider对象时未传入请求中的骑手字段数据:
在BaseUserRegistrationView的post方法中,创建Rider的代码是:

user_obj = self.user_model.objects.create(user=user)

仅传递了user关联字段,其余骑手专属字段(如vehicle_registration_number)未从request.data中提取并传入,导致Rider模型使用默认值或空值。

同时,UserSerializer仅验证和处理CustomUser的字段,不会处理Rider的字段,这些字段需要单独提取和验证。


解决方案

方案一:直接提取字段创建Rider(快速修复)

修改BaseUserRegistrationView的post方法,在创建Rider时传入请求中的骑手字段:

@transaction.atomic
def post(self, request, *args, **kwargs):
    logger.debug(f"Request_data: {request.data}")
    user_data = request.data
    
    # 分离CustomUser和Rider的字段
    rider_fields = [
        "vehicle_registration_number", 
        "min_capacity", 
        "max_capacity", 
        "fragile_item_allowed", 
        "charge_per_mile",
        "vehicle_type"  # 可选,若允许用户自定义车型
    ]
    rider_data = {key: request.data.get(key) for key in rider_fields if key in request.data}

    user_serializer = UserSerializer(data=user_data)

    if user_serializer.is_valid():
        user_data = user_serializer.validated_data
        if user_data:
            try:
                with transaction.atomic():
                    user = user_serializer.save()
                    # 传入rider_data创建Rider
                    user_obj = self.user_model.objects.create(user=user, **rider_data)
                    user_obj_serializer = self.serializer_class(user_obj).data

                    if user_obj_serializer:
                        send_verification_email(user, "registration")
                        return Response(
                            {
                                "data": user_obj_serializer,
                                "message": f"{self.user_model.__name__} registration successful",
                            },
                            status=status.HTTP_201_CREATED,
                        )
                    # ... 其余错误处理代码不变

方案二:使用RiderSerializer验证并创建(更规范)

这种方式利用DRF序列化器的验证能力,更符合REST框架的设计:

  1. 修改RiderSerializer,允许写入user字段(通过隐藏字段):
class RiderSerializer(serializers.ModelSerializer):
    user = CustomUserNestedSerializer(read_only=True)
    # 添加隐藏的user字段用于创建时关联
    user_id = serializers.PrimaryKeyRelatedField(
        queryset=CustomUser.objects.all(), write_only=True, source="user"
    )

    class Meta:
        model = Rider
        fields = [
            "user",
            "user_id",
            "is_available",
            "vehicle_type",
            "vehicle_registration_number",
            "min_capacity",
            "max_capacity",
            "fragile_item_allowed",
            "ratings",
            "charge_per_mile",
        ]
  1. 修改BaseUserRegistrationView的post方法:
@transaction.atomic
def post(self, request, *args, **kwargs):
    logger.debug(f"Request_data: {request.data}")
    
    # 先验证并创建User
    user_serializer = UserSerializer(data=request.data)
    if not user_serializer.is_valid():
        return Response(
            {"detail": "Validation error", "errors": user_serializer.errors},
            status=status.HTTP_400_BAD_REQUEST,
        )
    
    try:
        with transaction.atomic():
            user = user_serializer.save()
            
            # 准备Rider数据,添加user关联
            rider_data = request.data.copy()
            rider_data["user_id"] = user.id
            
            # 验证并创建Rider
            rider_serializer = self.serializer_class(data=rider_data)
            if not rider_serializer.is_valid():
                raise serializers.ValidationError(rider_serializer.errors)
            
            user_obj = rider_serializer.save()
            send_verification_email(user, "registration")
            
            return Response(
                {
                    "data": rider_serializer.data,
                    "message": f"{self.user_model.__name__} registration successful",
                },
                status=status.HTTP_201_CREATED,
            )
    
    except IntegrityError as e:
        logger.error(f"Integrity error: {e}")
        return Response(
            {"detail": "Integrity error. Please ensure the data is unique."},
            status=status.HTTP_400_BAD_REQUEST,
        )
    except serializers.ValidationError as e:
        return Response(
            {"detail": "Validation error", "errors": e.detail},
            status=status.HTTP_400_BAD_REQUEST,
        )
    except Exception as e:
        logger.error(f"An unexpected error occurred: {e}")
        return Response(
            {"detail": "An unexpected error occurred."},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR,
        )

额外优化:清理UserSerializer的字段验证

确保UserSerializer只处理CustomUser的字段,避免意外包含Rider字段。如果需要严格验证,可以添加代码检查未知字段:

class UserSerializer(serializers.ModelSerializer):
    # ... 现有代码
    def validate(self, data):
        # 检查是否有未知字段
        allowed_fields = set(self.fields.keys())
        input_fields = set(data.keys())
        unknown_fields = input_fields - allowed_fields
        if unknown_fields:
            raise serializers.ValidationError(f"Unknown fields: {', '.join(unknown_fields)}")
        # ... 现有密码验证代码

验证修改效果

重新发送请求后,响应中的骑手字段应正确返回请求传入的值:

{
    "data": {
        "user": {
            "email": "faruqmohammad@gmail.com",
            "first_name": "Faruq",
            "last_name": "Mohahmmad",
            "phone_number": "08137021976"
        },
        "is_available": true,
        "vehicle_type": "TWO_WHEELER",
        "vehicle_registration_number": "ABJ145",
        "min_capacity": 20,
        "max_capacity": 50,
        "fragile_item_allowed": true,
        "ratings": null,
        "charge_per_mile": "1000.00"
    },
    "message": "Rider registration successful"
}

内容的提问来源于stack exchange,提问作者Abdulmujeeb Ahmad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:04:49