FossilRepo

fossilrepo / core / models.py
Blame History Raw 75 lines
1
import uuid
2
3
from django.conf import settings
4
from django.db import models
5
from django.utils import timezone
6
from django.utils.text import slugify
7
from simple_history.models import HistoricalRecords
8
9
10
class Tracking(models.Model):
11
"""Abstract base providing audit trails and soft deletes for all business models."""
12
13
version = models.PositiveIntegerField(default=1, editable=False)
14
created_at = models.DateTimeField(auto_now_add=True)
15
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, related_name="+")
16
updated_at = models.DateTimeField(auto_now=True)
17
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, related_name="+")
18
deleted_at = models.DateTimeField(null=True, blank=True)
19
deleted_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, related_name="+")
20
history = HistoricalRecords(inherit=True)
21
22
class Meta:
23
abstract = True
24
25
def save(self, *args, **kwargs):
26
if self.pk:
27
self.version += 1
28
super().save(*args, **kwargs)
29
30
def soft_delete(self, user=None):
31
self.deleted_at = timezone.now()
32
self.deleted_by = user
33
self.save(update_fields=["deleted_at", "deleted_by", "updated_at", "version"])
34
35
@property
36
def is_deleted(self):
37
return self.deleted_at is not None
38
39
40
class BaseCoreModel(Tracking):
41
"""Abstract base for named, addressable entities with UUID external identifiers."""
42
43
guid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False, db_index=True)
44
name = models.CharField(max_length=200)
45
slug = models.SlugField(max_length=200, unique=True, db_index=True)
46
description = models.TextField(blank=True, default="")
47
48
class Meta:
49
abstract = True
50
51
def __str__(self):
52
return self.name
53
54
def save(self, *args, **kwargs):
55
if not self.slug:
56
base_slug = slugify(self.name)
57
slug = base_slug
58
counter = 1
59
model_class = type(self)
60
while model_class.objects.filter(slug=slug).exclude(pk=self.pk).exists():
61
slug = f"{base_slug}-{counter}"
62
counter += 1
63
self.slug = slug
64
super().save(*args, **kwargs)
65
66
def get_absolute_url(self):
67
return f"/{self._meta.app_label}/{self.slug}/"
68
69
70
class ActiveManager(models.Manager):
71
"""Manager that excludes soft-deleted records by default."""
72
73
def get_queryset(self):
74
return super().get_queryset().filter(deleted_at__isnull=True)
75

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button