Coverage for src / mesh / models / orm / base_models.py: 98%
46 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-05-04 12:41 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-05-04 12:41 +0000
1from __future__ import annotations
3from typing import TYPE_CHECKING
5from django.conf import settings
6from django.contrib.auth import get_user_model
7from django.db import models
8from django.utils import timezone
9from django.utils.translation import gettext_lazy as _
11if TYPE_CHECKING:
12 from .user_models import User
15class BaseChangeTrackingModel(models.Model):
16 """
17 Base model used to perform simple "change tracking" on models.
18 It adds 4 fields to the model:
19 - `date_created` The creation date of the object.
20 - `created_by` The user creating the object.
21 - `date_last_modified` The date of the last modification performed.
22 - `last_modified_by` The user who performed the last modification.
24 The data is automatically updated when calling the model `save()` method.
25 The code looks in the `_user` attribute to update the tracking fields. The views
26 must inject it in the instance before saving it.
27 """
29 # Instance of the user creating / modifying the model used to automatically fill
30 # the `created_by` and `last_modified_by` fields.
31 _user: User | None = None
32 # Whether to prevent the update of the last modification fields.
33 do_not_update = False
34 date_created = models.DateTimeField(
35 verbose_name=_("Creation date"),
36 default=timezone.now,
37 null=True,
38 editable=False,
39 )
40 created_by = models.ForeignKey["User"](
41 settings.AUTH_USER_MODEL,
42 verbose_name=_("Created by"),
43 on_delete=models.SET_NULL,
44 null=True,
45 help_text=_("Automatically filled on save."),
46 editable=False,
47 related_name="+",
48 )
49 date_last_modified = models.DateTimeField(
50 verbose_name=_("Last modification date"),
51 default=timezone.now,
52 null=True,
53 editable=False,
54 )
55 last_modified_by = models.ForeignKey["User"](
56 settings.AUTH_USER_MODEL,
57 verbose_name=_("Last modified by"),
58 on_delete=models.SET_NULL,
59 null=True,
60 help_text=_("Automatically filled on save."),
61 editable=False,
62 related_name="+",
63 )
65 class Meta:
66 abstract = True
68 def override_saved_date(
69 self,
70 *args,
71 date_created: timezone.datetime | None = None,
72 date_last_modified: timezone.datetime | None = None,
73 created_by_user: User | None = None,
74 last_modified_by_user: User | None = None,
75 ):
76 self.do_not_update = True
77 if date_created:
78 self.date_created = date_created
79 if date_last_modified:
80 self.date_last_modified = date_last_modified
81 if created_by_user:
82 self.created_by_id = created_by_user.pk
83 if last_modified_by_user:
84 self.last_modified_by_id = last_modified_by_user.pk
86 def save(self, *args, **kwargs) -> None:
87 """
88 Update the tracking data before save:
89 - Update the creation data if the object is new
90 - Update the last modification data if the `_request` attribute is set
91 """
92 UserModel = get_user_model()
93 if self.do_not_update:
94 return super().save(*args, **kwargs)
96 if self._state.adding:
97 self.date_created = timezone.now()
98 if self._user and isinstance(self._user, UserModel):
99 self.created_by_id = self._user.pk
101 if not self.do_not_update:
102 self.date_last_modified = timezone.now()
103 if self._user and isinstance(self._user, UserModel):
104 self.last_modified_by_id = self._user.pk
105 # self.date_last_modified = timezone.now()
106 # if self._user and isinstance(self._user, UserModel):
107 # self.last_modified_by_id = self._user.pk
109 super().save(*args, **kwargs)
112class BaseSubmittableModel(models.Model):
113 """
114 Base model "mixin" for model to be effectively submitted
115 (different action from just saving the model).
116 """
118 submitted = models.BooleanField(verbose_name=_("Submitted"), default=False, editable=False)
119 date_submitted = models.DateTimeField(
120 verbose_name=_("Submitted on"), editable=False, null=True
121 )
123 class Meta:
124 abstract = True
126 def is_submittable(self) -> bool:
127 return True