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

Django中IntegrityError:CHECK约束失败问题排查求助

问题分析与解决方案

根源定位

这个IntegrityError是因为数据库层面的CHECK约束被违反,核心原因有两点:

  1. 字段类型不匹配:CajaChica.disponible是PositiveIntegerField(整数类型),但Comprobante.importe是DecimalField(带小数的金额类型),两者运算后会产生小数无法存入整数字段,或扣减后数值变为负数,触发PositiveIntegerField的非负约束。
  2. 编辑场景重复扣减:编辑已存在的Comprobante时,原save方法会重复执行扣减逻辑,导致同一笔金额被多次扣除,最终让disponible变为负数,违反约束。

分步解决

1. 修正字段类型(核心)

将CajaChica中的金额字段改为DecimalField,和业务金额类型保持一致,避免精度丢失与类型冲突:

class CajaChica(models.Model):
    # ... 其他字段保持不变
    limite = models.DecimalField(
        max_digits=7, decimal_places=2,
        help_text='Cantidad de dinero máxima asignada para gastos'
    )
    disponible = models.DecimalField(
        max_digits=7, decimal_places=2,
        help_text='Dinero disponible para gastos'
    )
    # ... 其他字段保持不变

修改后执行数据库迁移:

python manage.py makemigrations
python manage.py migrate

2. 修复save方法逻辑(适配新增/编辑场景)

区分新增与编辑操作,添加事务保证原子性,同时验证扣减后金额非负:

from django.db import transaction, IntegrityError

class Comprobante(models.Model):
    # ... 其他字段与方法不变

    def save(self, *args, **kwargs):
        # 获取原记录金额(编辑场景用)
        original_importe = None
        if self.pk:
            original = Comprobante.objects.get(pk=self.pk)
            original_importe = original.importe

        # 先保存单据本身
        super().save(*args, **kwargs)

        try:
            with transaction.atomic():
                # 加锁获取最新的备用金实例,避免并发冲突
                caja = CajaChica.objects.select_for_update().get(pk=self.caja_chica.pk)
                
                if original_importe is not None:
                    # 编辑场景:先加回原金额
                    caja.disponible += original_importe
                
                # 扣减新金额
                caja.disponible -= self.importe

                # 验证余额非负
                if caja.disponible < 0:
                    raise IntegrityError("Saldo disponible insuficiente")
                
                caja.save()
        except IntegrityError as e:
            raise e

3. 前端前置验证(可选优化)

在表单层面提前拦截余额不足的情况,避免到数据库层才报错:

from django import forms
from .models import Comprobante

class ComprobanteForm(forms.ModelForm):
    class Meta:
        model = Comprobante
        fields = '__all__'

    def clean(self):
        cleaned_data = super().clean()
        caja_chica = cleaned_data.get('caja_chica')
        importe = cleaned_data.get('importe')

        if caja_chica and importe:
            # 计算编辑场景的金额差额
            if self.instance.pk:
                required = importe - self.instance.importe
            else:
                required = importe

            if caja_chica.disponible < required:
                raise forms.ValidationError(f"Saldo disponible insuficiente. Disponible: {caja_chica.disponible}")
        
        return cleaned_data

验证要点

  1. 新增单据:确认disponible正确扣减,无负数情况。
  2. 编辑单据:修改金额后,disponible会先回滚原金额再扣减新金额,数值准确。
  3. 超限提交:表单或数据库层会拦截余额不足的操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 08:07:53