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

求助:Django模型实现自动增减功能,删除后编号自动递减

Django实现故障记录编号自动递补(删除后自动重排)

首先明确:不要用主键(AutoField)来做这个故障编号,主键是数据库关联的核心标识,修改主键会引发外键关联异常,而且自增主键的设计本就不支持修改。我们单独用一个字段维护顺序,再生成格式化的编号。

1. 模型设计

新增sequence字段存储顺序整数,通过属性动态生成Mxx格式的故障编号:

from django.db import models
from django.db import transaction

class FaultRecord(models.Model):
    # 主键用默认自增,不修改它
    sequence = models.IntegerField(unique=True, blank=True, null=True)  # 维护顺序的整数
    description = models.TextField(verbose_name="故障描述")
    created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")

    @property
    def fault_number(self):
        # 格式化生成M01、M02...M10这类编号
        return f"M{self.sequence:02d}" if self.sequence else "未分配"

    def __str__(self):
        return self.fault_number

    def save(self, *args, **kwargs):
        # 新增时自动分配sequence,替代信号的写法(二选一即可)
        if self._state.adding and not self.sequence:
            with transaction.atomic():
                # 加锁避免并发新增导致sequence重复
                max_seq = FaultRecord.objects.select_for_update().aggregate(models.Max('sequence'))['sequence__max'] or 0
                self.sequence = max_seq + 1
        super().save(*args, **kwargs)

2. 删除后自动递补逻辑

方式一:用Django信号(全局生效)

在models.py或单独的signals.py中添加信号处理:

from django.db.models.signals import post_delete
from django.dispatch import receiver
from .models import FaultRecord

@receiver(post_delete, sender=FaultRecord)
def adjust_sequence_after_delete(sender, instance, **kwargs):
    # 删除记录后,所有sequence大于被删记录的,统一减1
    FaultRecord.objects.filter(sequence__gt=instance.sequence).update(sequence=models.F('sequence') - 1)

如果信号单独放signals.py,需要在apps.py中注册:

from django.apps import AppConfig

class YourAppConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'your_app'

    def ready(self):
        import your_app.signals

方式二:重写Admin删除方法(仅Admin后台生效)

如果只需要在Admin后台删除时生效,直接重写Admin类的删除方法,同时处理单条和批量删除:

from django.contrib import admin
from django.db.models import Count, Case, When, IntegerField
from .models import FaultRecord

@admin.register(FaultRecord)
class FaultRecordAdmin(admin.ModelAdmin):
    list_display = ('fault_number', 'description', 'created_at')
    readonly_fields = ('sequence',)  # 禁止手动修改sequence

    def delete_model(self, request, obj):
        # 单条删除逻辑
        deleted_seq = obj.sequence
        super().delete_model(request, obj)
        FaultRecord.objects.filter(sequence__gt=deleted_seq).update(sequence=models.F('sequence') - 1)

    def delete_queryset(self, request, queryset):
        # 批量删除逻辑
        deleted_seqs = queryset.values_list('sequence', flat=True)
        super().delete_queryset(request, queryset)
        # 计算每个后续记录需要减去的数量(比如删了3和5,sequence=6的要减2)
        FaultRecord.objects.annotate(
            subtract_count=Count(
                Case(
                    *[When(sequence__gt=s, then=1) for s in deleted_seqs],
                    output_field=IntegerField()
                )
            )
        ).update(sequence=models.F('sequence') - models.F('subtract_count'))

3. 注意事项

  • 并发问题:新增时用transaction.atomic()+select_for_update()加锁,防止多个用户同时新增导致sequence重复。
  • 批量删除:如果用信号处理批量删除,会为每个被删对象触发一次信号,逻辑依然成立,但重写Admin的delete_queryset效率更高。
  • 数据迁移:如果已有历史数据,需要先给现有记录批量赋值sequence(比如按创建时间排序,从1开始递增)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 09:13:27