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

如何在已有Django模型中添加带验证规则的自定义JSON字段?

两种实现方案,按需选择

方案一:使用JSONField + 模型级验证(适合灵活场景)

直接给Work模型添加JSONField,通过重写模型的clean()方法实现自定义验证规则,无需额外创建模型。

1. 修改models.py

from django.db import models
from django.core.exceptions import ValidationError

class Work(models.Model):
    id = CustomUUIDField(default=uuid.uuid4, unique=True, editable=False, primary_key=True)
    level = models.CharField(max_length=100, null=True, blank=True)
    is_paid = models.BooleanField(default=True)
    # 添加JSON字段,默认空字典
    is_experienced_required = models.JSONField(default=dict, null=True, blank=True)

    def clean(self):
        super().clean()
        exp_data = self.is_experienced_required or {}
        
        # 验证is_enabled必须为布尔值
        is_enabled = exp_data.get('is_enabled')
        if not isinstance(is_enabled, bool):
            raise ValidationError({
                'is_experienced_required': 'is_enabled必须为布尔值(True/False)'
            })
        
        # is_enabled为False时,unit和no必须为None
        if not is_enabled:
            if exp_data.get('unit') is not None or exp_data.get('no') is not None:
                raise ValidationError({
                    'is_experienced_required': '当is_enabled为False时,unit和no必须设为None'
                })
            return
        
        # 验证unit只能是指定选项或None
        allowed_units = ['days', 'weeks', 'months', None]
        unit = exp_data.get('unit')
        if unit not in allowed_units:
            raise ValidationError({
                'is_experienced_required': f'unit必须是{allowed_units[:-1]}中的一个或None'
            })
        
        # 验证no必须是正整数或None
        no = exp_data.get('no')
        if no is not None:
            if not isinstance(no, int) or no <= 0:
                raise ValidationError({
                    'is_experienced_required': 'no必须为正整数或None'
                })

    def save(self, *args, **kwargs):
        # 保存前强制触发验证
        self.full_clean()
        super().save(*args, **kwargs)

2. 执行数据库迁移

终端运行命令:

python manage.py makemigrations
python manage.py migrate

方案二:创建单独关联模型(适合结构固定、易维护场景)

如果需求长期固定,推荐这种方式——把JSON内的字段拆成独立模型字段,Django自动处理大部分验证,对新手更友好。

1. 修改models.py

from django.db import models
from django.core.exceptions import ValidationError

class ExperienceRequirement(models.Model):
    # 定义unit可选值
    UNIT_CHOICES = [
        ('days', '天'),
        ('weeks', '周'),
        ('months', '月'),
    ]
    is_enabled = models.BooleanField(default=False)
    unit = models.CharField(max_length=10, choices=UNIT_CHOICES, null=True, blank=True)
    no = models.PositiveIntegerField(null=True, blank=True)

    def clean(self):
        super().clean()
        # 仅需验证is_enabled为False时,unit和no必须为空
        if not self.is_enabled:
            if self.unit is not None or self.no is not None:
                raise ValidationError('当is_enabled为False时,unit和no必须为空')

class Work(models.Model):
    id = CustomUUIDField(default=uuid.uuid4, unique=True, editable=False, primary_key=True)
    level = models.CharField(max_length=100, null=True, blank=True)
    is_paid = models.BooleanField(default=True)
    # 一对一关联到ExperienceRequirement
    is_experienced_required = models.OneToOneField(
        ExperienceRequirement,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='work'
    )

2. 执行数据库迁移

同样运行:

python manage.py makemigrations
python manage.py migrate

方案对比

  • 方案一:优点是灵活,后续修改JSON结构无需变更数据库;缺点是需自定义验证逻辑,admin后台默认是文本框(优化需自定义表单),查询JSON内字段效率低于原生字段。
  • 方案二:优点是字段类型明确,Django自动验证,admin后台自动生成对应表单控件,查询效率高;缺点是结构固定,修改字段需做数据库迁移。

作为Django新手,优先推荐方案二,更贴合Django ORM设计思想,减少自定义代码,降低出错概率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 10:01:28