Django模型实现固定格式唯一作业编码一次性生成的问题
解决Django Job模型作业编码自动生成与不变性问题
核心思路
仅在Job实例首次创建且未手动指定编码时生成唯一作业编码,后续任何操作(关联Cost、修改作业日期)都保留原有编码;同时支持手动覆盖编码,并通过字段标记编码来源。
具体实现步骤
1. 修改Job模型
添加必要字段并优化save()方法,确保编码仅生成一次:
from django.db import models, IntegrityError, transaction from django.utils.timezone import now class Client(models.Model): code = models.CharField(max_length=3, unique=True) # 客户3位标识字段 # 其他客户相关字段... class Job(models.Model): client = models.ForeignKey(Client, on_delete=models.CASCADE) job_date = models.DateField() job_code = models.CharField(max_length=11, unique=True, blank=True, null=True) # 匹配aaaMMnnYYYY格式长度 code_generated_date = models.DateField(blank=True, null=True) # 记录编码生成时的日期,后续固定不变 is_auto_generated = models.BooleanField(default=False) # 标记编码是否为自动生成 def save(self, *args, **kwargs): # 仅在首次创建且未手动设置job_code时自动生成编码 if not self.pk and not self.job_code: # 确定编码依赖的年月:优先用当前job_date,无则取系统当前日期 self.code_generated_date = self.job_date or now().date() year = self.code_generated_date.year month = self.code_generated_date.month client_code = self.client.code # 处理并发冲突,最多重试3次 max_retries = 3 for attempt in range(max_retries): try: with transaction.atomic(): # 实时统计当月该客户的作业数量,避免并发导致编码重复 count = Job.objects.filter( client=self.client, code_generated_date__year=year, code_generated_date__month=month ).count() # 序号补两位(如3→03) sequence = f"{count + 1:02d}" # 拼接生成编码 self.job_code = f"{client_code}{month:02d}{sequence}{year}" self.is_auto_generated = True super().save(*args, **kwargs) break except IntegrityError: if attempt == max_retries - 1: raise # 重试失败,抛出原错误 # 重试时重新计算计数 continue elif not self.pk and self.job_code: # 手动指定编码的场景 self.is_auto_generated = False super().save(*args, **kwargs) else: # 已有编码的实例,直接保存不修改编码 super().save(*args, **kwargs)
2. 解决关联Cost时的编码变更问题
之前的问题根源是每次save()都重新生成编码,现在仅在首次创建时触发生成逻辑,后续关联Cost(无论修改Cost还是通过Cost关联Job)都不会触发编码重新计算,彻底规避唯一性错误和URL失效问题。
3. 作业日期修改不影响编码
code_generated_date字段仅在首次创建时赋值,后续修改job_date不会改变该字段,编码生成逻辑依赖的是code_generated_date的年月,因此编码始终保持不变。
4. 支持手动覆盖编码
job_code字段设置为blank=True, null=True,在Admin后台或自定义表单中可直接手动输入编码,保存时会跳过自动生成逻辑,直接使用手动输入的值,并将is_auto_generated标记为False。
5. Admin后台配置(可选)
如果需要在后台管理中控制编码的编辑权限,可配置Job的Admin类:
from django.contrib import admin from .models import Job class JobAdmin(admin.ModelAdmin): list_display = ('job_code', 'client', 'job_date', 'is_auto_generated') fields = ('client', 'job_date', 'job_code', 'is_auto_generated') readonly_fields = ('is_auto_generated',) # 禁止修改编码来源标记 admin.site.register(Job, JobAdmin)
关键说明
- 并发处理:通过事务+重试机制避免多请求同时创建同客户当月作业时的编码重复问题
- 字段标记:
is_auto_generated字段可用于后续业务逻辑区分自动/手动编码(比如禁止修改自动生成的编码,或做特殊校验) - URL稳定性:编码一旦生成不再变更,作为URL参数使用时不会出现链接失效问题
内容的提问来源于stack exchange,提问作者jconnorbuilds
相关产品推荐
相关产品推荐

