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

Django序列化器报错:Field 'id'期望数字却得到<Customer: Lary>

问题:Django序列化器单元测试报错"Field 'id' expected a number but got <Customer: Lary>"

我定义了如下Django模型和序列化器,编写单元测试验证处方时长超过90天会校验失败时,意外抛出错误Field 'id' expected a number but got <Customer: Lary>。明明测试代码里传入的是customer.id而非Customer对象,且之前功能正常,现在突然无法运行。

相关代码

模型代码

class ChronicPrescription(models.Model):
    chronic_prescription_id = models.AutoField(primary_key=True)
    date = models.DateField(default=datetime.date.today, validators=[no_future_date, no_old_date])
    # 处方时长(天)
    duration = models.PositiveIntegerField(default=90, validators=[MaxValueValidator(90), MinValueValidator(30)])
    customer = models.ForeignKey('customers.Customer', on_delete=models.CASCADE, related_name="chronic_prescription", 
                                 validators=[prescriptions_for_patient_only])


class Customer(models.Model):
    id = models.BigAutoField(primary_key=True)
    customer_name = models.CharField(max_length=100, null=False, blank=False, unique=True)
    phones = ArrayField(models.CharField(max_length=10, validators=[validate_phone_number, prevent_replicated_phone]), 
                                        default=list, null=True, blank=True)
    customer_type = models.CharField(max_length=10,default='patient', choices=CUSTOMER_TYPE)

序列化器代码

class ChronicPrescriptionSerializer(serializers.ModelSerializer):
    drugs = PrescriptionItemSerializer(many=True, read_only=True)
    notification_status = serializers.BooleanField(default=True)

    left_days = serializers.SerializerMethodField()
    def get_left_days(self, obj):
        return obj.count_left_days()

    class Meta:
        model = ChronicPrescription
        fields = ('chronic_prescription_id', 'date', 'duration', 'notification_status', 'left_days', 'drugs', 'customer') 

单元测试代码

def test_upper_bound_duration(self):
    customer = Customer.objects.create(customer_name="Lary")
    prescr_serializer = ChronicPrescriptionSerializer(data={'duration': 1000, 'customer': customer.id})
    
    if prescr_serializer.is_valid():
        prescr_serializer.save()
    self.assertFalse(prescr_serializer.is_valid())
    
    self.assertEqual(set(prescr_serializer.errors), set(['duration'])) 

解决方法

1. 显式指定序列化器的customer字段类型

ModelSerializer默认会将外键字段处理为PrimaryKeyRelatedField,但如果项目中有基类序列化器、信号或者其他代码隐式修改了这个字段的类型,就会导致传入ID时报错。直接显式指定字段类型可以避免这个问题:

class ChronicPrescriptionSerializer(serializers.ModelSerializer):
    drugs = PrescriptionItemSerializer(many=True, read_only=True)
    notification_status = serializers.BooleanField(default=True)
    # 显式指定为PrimaryKeyRelatedField,确保接受ID值
    customer = serializers.PrimaryKeyRelatedField(queryset=Customer.objects.all())
    left_days = serializers.SerializerMethodField()
    
    def get_left_days(self, obj):
        return obj.count_left_days()
    
    class Meta:
        model = ChronicPrescription
        fields = ('chronic_prescription_id', 'date', 'duration', 'notification_status', 'left_days', 'drugs', 'customer') 

2. 检查prescriptions_for_patient_only验证器逻辑

这个自定义验证器很可能是问题根源:如果验证器在处理过程中返回了Customer对象而非原始的ID值,序列化器后续处理就会拿到对象而非数字,从而触发报错。验证器必须返回原始输入值,不能返回对象:

def prescriptions_for_patient_only(value):
    # value是传入的customer ID,先查询对象
    customer = Customer.objects.get(id=value)
    if customer.customer_type != 'patient':
        raise serializers.ValidationError("仅患者可创建慢性病处方")
    # 必须返回原始的value,不能返回customer对象
    return value

3. 优化测试代码的is_valid()调用

测试代码中重复调用了is_valid(),虽然通常不会有问题,但可能触发验证器的副作用。改成只调用一次并保存结果:

def test_upper_bound_duration(self):
    customer = Customer.objects.create(customer_name="Lary")
    prescr_serializer = ChronicPrescriptionSerializer(data={'duration': 1000, 'customer': customer.id})
    
    # 只调用一次is_valid(),避免重复执行验证逻辑
    is_valid = prescr_serializer.is_valid()
    self.assertFalse(is_valid)
    self.assertEqual(set(prescr_serializer.errors), set(['duration']))

4. 排查依赖版本变更

如果最近更新了Django或Django REST Framework版本,可能存在序列化器字段处理的兼容性问题。可以暂时回退到之前的稳定版本,确认是否是版本更新导致的问题。


内容的提问来源于stack exchange,提问作者khelalfa rabah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 04:50:30