Coverage for src/mesh/models/base_models.py: 94%
37 statements
« prev ^ index » next coverage.py v7.7.0, created at 2025-04-28 07:45 +0000
« prev ^ index » next coverage.py v7.7.0, created at 2025-04-28 07:45 +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: 11 ↛ 12line 11 didn't jump to line 12 because the condition on line 11 was never true
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 blank=True,
38 null=True,
39 editable=False,
40 )
41 created_by = models.ForeignKey(
42 settings.AUTH_USER_MODEL,
43 verbose_name=_("Created by"),
44 on_delete=models.SET_NULL,
45 blank=True,
46 null=True,
47 help_text=_("Automatically filled on save."),
48 editable=False,
49 related_name="+",
50 )
51 date_last_modified = models.DateTimeField(
52 verbose_name=_("Last modification date"),
53 default=timezone.now,
54 blank=True,
55 null=True,
56 editable=False,
57 )
58 last_modified_by = models.ForeignKey(
59 settings.AUTH_USER_MODEL,
60 verbose_name=_("Last modified by"),
61 on_delete=models.SET_NULL,
62 blank=True,
63 null=True,
64 help_text=_("Automatically filled on save."),
65 editable=False,
66 related_name="+",
67 )
69 class Meta:
70 abstract = True
72 def save(self, *args, **kwargs) -> None:
73 """
74 Update the tracking data before save:
75 - Update the creation data if the object is new
76 - Update the last modification data if the `_request` attribute is set
77 """
78 UserModel = get_user_model()
79 if self._state.adding:
80 self.date_created = timezone.now()
81 if self._user and isinstance(self._user, UserModel):
82 self.created_by_id = self._user.pk
84 if not self.do_not_update:
85 self.date_last_modified = timezone.now()
86 if self._user and isinstance(self._user, UserModel):
87 self.last_modified_by_id = self._user.pk
89 super().save(*args, **kwargs)
92class BaseSubmittableModel(models.Model):
93 """
94 Base model "mixin" for model to be effectively submitted
95 (different action from just saving the model).
96 """
98 submitted = models.BooleanField(
99 verbose_name=_("Submitted"), default=False, editable=False, blank=True
100 )
101 date_submitted = models.DateTimeField(
102 verbose_name=_("Submitted on"), editable=False, blank=True, null=True
103 )
105 class Meta:
106 abstract = True
108 @property
109 def is_submittable(self) -> bool:
110 return True