|
| 1 | +import math |
| 2 | +import secrets |
| 3 | +from typing import Any |
| 4 | + |
1 | 5 | from django.apps import apps |
| 6 | +from django.db import models |
2 | 7 | from simple_history.models import HistoricalRecords, registered_models |
3 | 8 |
|
4 | 9 |
|
@@ -57,3 +62,29 @@ def create_proxy_history_model(self, model, inherited, base_history): |
57 | 62 | name = self.get_history_model_name(model) |
58 | 63 | registered_models[opts.db_table] = model |
59 | 64 | return type(str(name), (base_history,), attrs) |
| 65 | + |
| 66 | + |
| 67 | +class ShortIDField(models.CharField): |
| 68 | + """ |
| 69 | + A custom model field that generates a short, URL-safe identifier. |
| 70 | + """ |
| 71 | + |
| 72 | + def __init__(self, size: int, *args, **kwargs) -> None: |
| 73 | + if not isinstance(size, int) or size <= 0: |
| 74 | + raise ValueError("Size must be a positive integer.") |
| 75 | + # See: https://zelark.github.io/nano-id-cc/ |
| 76 | + self.size = size |
| 77 | + kwargs["max_length"] = size # Assign max_length from size |
| 78 | + kwargs["default"] = self.generate_short_id # Use the method to generate the default value |
| 79 | + super().__init__(*args, **kwargs) |
| 80 | + |
| 81 | + def generate_short_id(self): |
| 82 | + """Generates a URL-safe short ID of specified length.""" |
| 83 | + return secrets.token_urlsafe(nbytes=math.ceil(self.size / 1.3))[: self.size] |
| 84 | + |
| 85 | + def deconstruct(self) -> Any: |
| 86 | + name, path, args, kwargs = super().deconstruct() |
| 87 | + kwargs["size"] = self.size |
| 88 | + del kwargs["max_length"] |
| 89 | + del kwargs["default"] |
| 90 | + return name, path, args, kwargs |
0 commit comments