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

Django Admin带条件UniqueConstraint模型提交报IntegrityError如何处理

解决方案

问题原因

Django的ModelForm默认仅会自动校验不带条件的唯一性约束,你定义的带condition参数的条件唯一约束不会触发表单层面的前置校验,重复提交时会直接触发数据库层面的IntegrityError异常,无法展示友好提示。

具体修改步骤

步骤1:修改Payment模型,添加校验逻辑

在models.py的Payment类中添加clean方法,提前做业务逻辑校验和唯一性校验,代码如下:

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

class Payment(DiModel):
    class Meta:
        # 可删除原无用的unique_together配置
        # unique_together = ('transaction_id', 'payment_id', 'operation', 'payment_created_date')
        constraints=[
            UniqueConstraint(fields=['transaction_id', 'operation', 'payment_created_date'],
                             condition=Q(payment_id=None),
                             name='unique_finance_payment_without_payment_id'),
            UniqueConstraint(fields=['payment_id', 'operation', 'payment_created_date'],
                             condition=Q(transaction_id=None),
                             name='unique_finance_payment_without_transaction_id'),
        ]

    # 原有字段、枚举等代码保持不变,新增clean方法
    def clean(self):
        super().clean()
        # 校验transaction_id和payment_id二选一必填
        if not self.transaction_id and not self.payment_id:
            raise ValidationError("transaction_id和payment_id必须填写其中一个")
        if self.transaction_id and self.payment_id:
            raise ValidationError("transaction_id和payment_id不能同时填写")
        
        # 校验组合唯一约束
        if self.transaction_id:
            # 校验transaction_id组合是否重复,排除当前编辑的记录
            duplicate = Payment.objects.filter(
                transaction_id=self.transaction_id,
                operation=self.operation,
                payment_created_date=self.payment_created_date,
                payment_id__isnull=True
            ).exclude(pk=self.pk).exists()
            if duplicate:
                raise ValidationError("已存在相同Transaction id、操作类型、支付创建日期的付款记录")
        else:
            # 校验payment_id组合是否重复,排除当前编辑的记录
            duplicate = Payment.objects.filter(
                payment_id=self.payment_id,
                operation=self.operation,
                payment_created_date=self.payment_created_date,
                transaction_id__isnull=True
            ).exclude(pk=self.pk).exists()
            if duplicate:
                raise ValidationError("已存在相同Payment id、操作类型、支付创建日期的付款记录")

步骤2:修改PaymentAdmin,兜底捕获数据库异常

为了避免并发场景下(两个请求同时通过前置校验,同时插入数据)仍然触发数据库唯一约束错误,在admin.py中重写save_model方法捕获IntegrityError,转换为表单错误提示:

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

@admin.register(Payment)
class PaymentAdmin(ImportExportMixin, admin.ModelAdmin):
    # 原有form、list_display等配置保持不变
    form = PaymentForm
    list_display = [
        'report_name',
        'operation',
        'payment_created_date',
        'amount',
        'commission',
        'currency',
        'transaction_id',
        'payment_id',
    ]
    # 其他原有配置...

    # 新增save_model方法
    def save_model(self, request, obj, form, change):
        try:
            super().save_model(request, obj, form, change)
        except IntegrityError as e:
            error_msg = str(e)
            # 匹配对应的唯一约束错误,返回对应提示
            if 'unique_finance_payment_without_payment_id' in error_msg:
                form.add_error(None, "已存在相同Transaction id、操作类型、支付创建日期的付款记录")
            elif 'unique_finance_payment_without_transaction_id' in error_msg:
                form.add_error(None, "已存在相同Payment id、操作类型、支付创建日期的付款记录")
            else:
                form.add_error(None, f"数据保存失败:{error_msg}")
            # 抛出校验错误,触发Admin表单错误提示
            raise ValidationError(form.errors)

效果说明

修改后,普通重复提交场景会在表单层面直接触发错误提示,不会走到数据库写入步骤;极端并发场景也会捕获数据库异常,展示友好的错误提示,不会直接抛出500异常页面。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 03:15:00