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
相关产品推荐
相关产品推荐

