Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Imports
3from django.conf import settings
4from django.db import models
5from django.utils.timezone import now
6from django.utils.translation import gettext_lazy as _
7from superdjango.shortcuts import get_user_name
8from ..datetime.fields import AutoNowAddDateTimeField, AutoNowDateTimeField
9from .mixins import AuditMixin
11# Exports
13__all__ = (
14 "AddedByModel",
15 "ModifiedByModel",
16 "ViewedByModel",
17)
19# Constants
21AUTH_USER_MODEL = settings.AUTH_USER_MODEL
23# Models
26class AddedByModel(AuditMixin, models.Model):
27 """Tracks the user that added a record as well as the date and time the record was created.
29 **Fields**
31 This class will add the following fields:
33 - ``added_by`` The user that added the record.
34 - ``added_dt`` The date and time the record was added.
36 **Proper Name**
38 The ``added_by_name`` property is also provided which attempts to return the full name of the user.
40 **Implementation**
42 .. code-block:: py
44 class Project(AddedByModel):
46 # ...
48 class Meta:
49 get_latest_by = "added_dt"
50 ordering = ["-added_dt"]
52 """
54 # blank must be True to allow form submission.
55 added_by = models.ForeignKey(
56 AUTH_USER_MODEL,
57 blank=True,
58 on_delete=models.PROTECT,
59 related_name="%(app_label)s_%(class)s_added_records",
60 verbose_name=_("added by")
61 )
63 added_dt = AutoNowAddDateTimeField(
64 _("added date/time"),
65 help_text=_("Date and time the record was created.")
66 )
68 class Meta:
69 abstract = True
71 def audit(self, user, commit=True):
72 """Check for ``added_by_id`` just in case the primary key is being managed at run time.
74 A ``RelatedObjectDoesNotExist`` error will be thrown if we just look for ``added_by``, so the simplest thing to
75 do appears to be checking for the primary key instead.
77 """
78 # noinspection PyUnresolvedReferences
79 if not self.pk or not self.added_by_id:
80 self.added_by = user
82 super().audit(user, commit=commit)
84 @property
85 def added_by_name(self):
86 """Get the full name or user name of the user that added the record.
88 :rtype: str
90 """
91 return get_user_name(self.added_by)
94class ModifiedByModel(AuditMixin):
95 """Tracks the user that last updated a record as well as the date and time of the last update.
97 **Fields**
99 This class will add the following fields:
101 - ``modified_by`` The user that modified record.
102 - ``modified_dt`` The date and time the modified was added.
104 **Proper Name**
106 The ``modified_by_name`` property is also provided which attempts to return the full name of the user.
108 **Implementation**
110 .. code-block:: py
112 class MyModel(ModifiedByModel):
114 # ...
116 class Meta:
117 ordering = ["modified_dt"]
119 """
121 # blank must be True to allow form submission
122 modified_by = models.ForeignKey(
123 AUTH_USER_MODEL,
124 blank=True,
125 on_delete=models.PROTECT,
126 related_name="%(app_label)s_%(class)s_modified_records",
127 verbose_name=_("modified by")
128 )
130 modified_dt = AutoNowDateTimeField(
131 _("modified date/time"),
132 help_text=_("Date and time the record was last modified.")
133 )
135 class Meta:
136 abstract = True
138 def audit(self, user, commit=True):
139 """Always sets ``modified_by`` to the given user."""
140 self.modified_by = user
142 super().audit(user, commit=commit)
144 @property
145 def modified_by_name(self):
146 """Get the full name or user name of the user that last modified the record.
148 :rtype: str
150 """
151 return get_user_name(self.modified_by)
154class ViewedByModel(AuditMixin):
155 """Tracks the user that view a record as well as the date and time the record was last viewed.
157 **Fields**
159 This class will add the following fields:
161 - ``viewed_by`` The user that viewed record.
162 - ``viewed_dt`` The date and time the record was last viewed.
164 **Proper Name**
166 The ``viewed_by_name`` property is also provided which attempts to return the full name of the user.
168 **Implementation**
170 .. code-block:: python
172 class MedicalRecord(ViewedByModel):
173 # ...
175 """
177 viewed_by = models.ForeignKey(
178 AUTH_USER_MODEL,
179 blank=True,
180 null=True,
181 on_delete=models.SET_NULL,
182 related_name="%(app_label)s_%(class)s_viewed_records",
183 verbose_name=_("viewed by")
184 )
186 viewed_dt = models.DateTimeField(
187 _("viewed date/time"),
188 blank=True,
189 help_text=_("Date and time the record was last viewed."),
190 null=True
191 )
193 class Meta:
194 abstract = True
196 def audit(self, user, commit=True):
197 """Update viewed fields.
199 .. important::
200 This method makes calls to ``super()`` which may also trigger the update of other fields if the model also
201 extends :py:class:`AddedByModel` or :py:class:`ModifiedByModel`. See ``mark_viewed()`` if you just want to
202 update the ``viewed_by`` and ``viewed_dt`` fields.
204 """
205 self.viewed_by = user
206 self.viewed_dt = now()
208 super().audit(user, commit=commit)
210 def mark_viewed(self, user, commit=True):
211 """Mark the record as viewed.
213 :param user: The user that reviewed the record.
214 :type user: AUTH_USER_MODEL
216 :param commit: Indicates the record should be saved after updating the field.
217 :type commit: bool
219 """
220 self.viewed_by = user
221 self.viewed_dt = now()
223 if commit:
224 self.save(update_fields=["viewed_by", "viewed_dt"])
226 def mark_unviewed(self, commit=True):
227 """Mark the record as "un-viewed", removing the last viewed data.
229 :param commit: Indicates the record should be saved after updating the field.
230 :type commit: bool
232 """
233 self.viewed_by = None
234 self.viewed_dt = None
236 if commit:
237 self.save(update_fields=["viewed_by", "viewed_dt"])
239 @property
240 def viewed_by_name(self):
241 """Get the full name or user name of the user that last viewed the record.
243 :rtype: str
245 """
246 return get_user_name(self.viewed_by)