求助: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
相关产品推荐
相关产品推荐

