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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 21:52:58