Django模型持续接收非法值问题:性别字段未校验有效选项
Django模型校验choices字段有效值的方法
Django的choices字段仅用于表单渲染选项和提供代码层面的枚举规范,默认不会在模型保存时自动校验值的合法性,所以你直接传入非法值也能保存成功。要实现模型层面的校验,有两种常用方案:
方案一:重写模型的clean方法
通过重写模型的clean钩子方法,在保存前主动校验字段值,同时重写save方法确保校验逻辑执行:
from django.db import models from django.core.exceptions import ValidationError class PetSex(models.TextChoices): MALE = 'Male', 'Male' FEMALE = 'Female', 'Female' NOT_INFORMED = 'Not Informed', 'Not Informed' class Pet(models.Model): name = models.CharField(max_length=50) age = models.IntegerField() weight = models.FloatField() sex = models.CharField(max_length=20, choices=PetSex.choices, default=PetSex.NOT_INFORMED) def clean(self): # 先执行父类的clean逻辑 super().clean() # 校验sex是否在合法选项中 valid_sexes = dict(PetSex.choices).keys() if self.sex not in valid_sexes: raise ValidationError({ 'sex': f"无效的性别值,可选值为: {', '.join(valid_sexes)}" }) def save(self, *args, **kwargs): # 强制调用full_clean触发所有校验(包括clean方法和validators) self.full_clean() super().save(*args, **kwargs) def __str__(self): return self.name
测试效果:
>>> from pets.models import Pet >>> petinfos_2 = {"name": "xamaria", "age": 1, "weight": 30, "sex": "Malasdasdde"} >>> test123 = Pet(**petinfos_2) >>> test123.save() ValidationError: {'sex': ["无效的性别值,可选值为: Male, Female, Not Informed"]}
方案二:使用自定义校验器
把校验逻辑抽成独立的校验器函数,通过字段的validators参数绑定,同样需要在save中调用full_clean:
from django.db import models from django.core.exceptions import ValidationError def validate_pet_sex(value): valid_sexes = dict(PetSex.choices).keys() if value not in valid_sexes: raise ValidationError(f"无效的性别值,可选值为: {', '.join(valid_sexes)}") class PetSex(models.TextChoices): MALE = 'Male', 'Male' FEMALE = 'Female', 'Female' NOT_INFORMED = 'Not Informed', 'Not Informed' class Pet(models.Model): name = models.CharField(max_length=50) age = models.IntegerField() weight = models.FloatField() sex = models.CharField( max_length=20, choices=PetSex.choices, default=PetSex.NOT_INFORMED, validators=[validate_pet_sex] ) def save(self, *args, **kwargs): self.full_clean() super().save(*args, **kwargs) def __str__(self): return self.name
注意事项
- 若使用Django表单(如ModelForm),表单会自动校验
choices字段的合法性,但直接通过模型实例保存(如shell中操作)时,必须手动调用full_clean或在save中集成,否则校验不会触发。 - 两种方案都能实现模型层面的校验,方案一更适合模型专属的校验逻辑,方案二更适合需要复用的校验规则。
内容的提问来源于stack exchange,提问作者NewDev
相关产品推荐
相关产品推荐

