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

如何实现Django模型字段仅创建可设、更新时禁止修改?

解决Django模型字段创建后只读的问题

你用editable=False只能让字段在表单和Admin界面不可编辑,但代码层面仍然可以修改字段值并保存,这就是为什么你还能更新该字段的原因。要实现「创建时可设置,更新时修改就报错」的需求,需要在模型的保存逻辑里做校验,下面是两种可行方案:

方案一:重写模型的save方法

直接在模型内部重写save方法,判断当前是创建还是更新操作,更新时检查字段是否被修改:

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

class BalanceHoldAmounts(models.Model):
    # 可保留editable=False,进一步限制表单层面的修改
    read_only_field = models.DecimalField(max_digits=10, decimal_places=2, editable=False)

    def save(self, *args, **kwargs):
        # 仅当是更新操作(对象已存在于数据库)时做校验
        if self.pk is not None:
            # 获取数据库中存储的原始对象
            original = BalanceHoldAmounts.objects.get(pk=self.pk)
            # 对比原始值和当前值,不一致则抛出异常
            if original.read_only_field != self.read_only_field:
                raise ValidationError("read_only_field字段不允许修改")
        # 调用父类的save方法完成保存
        super().save(*args, **kwargs)

方案二:使用pre_save信号

通过Django的信号机制,在对象保存前触发校验逻辑,适合不想修改模型save方法的场景:

from django.db import models
from django.core.exceptions import ValidationError
from django.db.models.signals import pre_save
from django.dispatch import receiver

class BalanceHoldAmounts(models.Model):
    read_only_field = models.DecimalField(max_digits=10, decimal_places=2, editable=False)

# 注册pre_save信号的接收器
@receiver(pre_save, sender=BalanceHoldAmounts)
def check_read_only_field(sender, instance, **kwargs):
    if instance.pk is not None:
        original = sender.objects.get(pk=instance.pk)
        if original.read_only_field != instance.read_only_field:
            raise ValidationError("read_only_field字段禁止修改")

注意事项

  • 如果你使用bulk_update批量更新对象,上述两种方案都不会生效(因为bulk_update不会调用单个对象的save方法,也不会触发pre_save信号),这种情况需要在执行bulk_update前自行遍历实例,检查字段是否被修改。
  • editable=False可以保留,它能让字段在Admin和自动生成的表单中隐藏,避免用户通过界面尝试修改。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 18:45:27