Files
funnywolf b6b4cbf017 0.4.0 I always have a choice
You shape nothing until you hold everything.
2026-06-27 19:08:48 +08:00

47 lines
1.5 KiB
Python

from django.db import IntegrityError, transaction
READABLE_ID_WIDTH = 6
READABLE_ID_RETRIES = 3
def format_readable_id(prefix: str, number: int) -> str:
return f"{prefix}_{number:0{READABLE_ID_WIDTH}d}"
def parse_readable_id_number(value: str | None, prefix: str) -> int:
if not value:
return 0
marker = f"{prefix}_"
if not value.startswith(marker):
return 0
suffix = value[len(marker):]
return int(suffix) if suffix.isdigit() else 0
def next_readable_id(model_class, field_name: str, prefix: str) -> str:
values = model_class.objects.exclude(**{field_name: ""}).values_list(field_name, flat=True)
max_number = 0
for value in values:
max_number = max(max_number, parse_readable_id_number(value, prefix))
return format_readable_id(prefix, max_number + 1)
def assign_readable_id(instance, field_name: str, prefix: str) -> None:
if getattr(instance, field_name):
return
setattr(instance, field_name, next_readable_id(type(instance), field_name, prefix))
def save_with_readable_id(instance, field_name: str, prefix: str, *args, **kwargs):
for attempt in range(READABLE_ID_RETRIES):
assign_readable_id(instance, field_name, prefix)
try:
with transaction.atomic():
return super(type(instance), instance).save(*args, **kwargs)
except IntegrityError:
if attempt == READABLE_ID_RETRIES - 1:
raise
setattr(instance, field_name, "")
return super(type(instance), instance).save(*args, **kwargs)