# Imports
from django.conf import settings
from django.db import models
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from superdjango.shortcuts import get_user_name
from ..datetime.fields import AutoNowAddDateTimeField, AutoNowDateTimeField
from .mixins import AuditMixin
# Exports
__all__ = (
"AddedByModel",
"ModifiedByModel",
"ViewedByModel",
)
# Constants
AUTH_USER_MODEL = settings.AUTH_USER_MODEL
# Models
[docs]class AddedByModel(AuditMixin, models.Model):
"""Tracks the user that added a record as well as the date and time the record was created.
**Fields**
This class will add the following fields:
- ``added_by`` The user that added the record.
- ``added_dt`` The date and time the record was added.
**Proper Name**
The ``added_by_name`` property is also provided which attempts to return the full name of the user.
**Implementation**
.. code-block:: py
class Project(AddedByModel):
# ...
class Meta:
get_latest_by = "added_dt"
ordering = ["-added_dt"]
"""
# blank must be True to allow form submission.
added_by = models.ForeignKey(
AUTH_USER_MODEL,
blank=True,
on_delete=models.PROTECT,
related_name="%(app_label)s_%(class)s_added_records",
verbose_name=_("added by")
)
added_dt = AutoNowAddDateTimeField(
_("added date/time"),
help_text=_("Date and time the record was created.")
)
class Meta:
abstract = True
[docs] def audit(self, user, commit=True):
"""Check for ``added_by_id`` just in case the primary key is being managed at run time.
A ``RelatedObjectDoesNotExist`` error will be thrown if we just look for ``added_by``, so the simplest thing to
do appears to be checking for the primary key instead.
"""
# noinspection PyUnresolvedReferences
if not self.pk or not self.added_by_id:
self.added_by = user
super().audit(user, commit=commit)
@property
def added_by_name(self):
"""Get the full name or user name of the user that added the record.
:rtype: str
"""
return get_user_name(self.added_by)
[docs]class ModifiedByModel(AuditMixin):
"""Tracks the user that last updated a record as well as the date and time of the last update.
**Fields**
This class will add the following fields:
- ``modified_by`` The user that modified record.
- ``modified_dt`` The date and time the modified was added.
**Proper Name**
The ``modified_by_name`` property is also provided which attempts to return the full name of the user.
**Implementation**
.. code-block:: py
class MyModel(ModifiedByModel):
# ...
class Meta:
ordering = ["modified_dt"]
"""
# blank must be True to allow form submission
modified_by = models.ForeignKey(
AUTH_USER_MODEL,
blank=True,
on_delete=models.PROTECT,
related_name="%(app_label)s_%(class)s_modified_records",
verbose_name=_("modified by")
)
modified_dt = AutoNowDateTimeField(
_("modified date/time"),
help_text=_("Date and time the record was last modified.")
)
class Meta:
abstract = True
[docs] def audit(self, user, commit=True):
"""Always sets ``modified_by`` to the given user."""
self.modified_by = user
super().audit(user, commit=commit)
@property
def modified_by_name(self):
"""Get the full name or user name of the user that last modified the record.
:rtype: str
"""
return get_user_name(self.modified_by)
[docs]class ViewedByModel(AuditMixin):
"""Tracks the user that view a record as well as the date and time the record was last viewed.
**Fields**
This class will add the following fields:
- ``viewed_by`` The user that viewed record.
- ``viewed_dt`` The date and time the record was last viewed.
**Proper Name**
The ``viewed_by_name`` property is also provided which attempts to return the full name of the user.
**Implementation**
.. code-block:: python
class MedicalRecord(ViewedByModel):
# ...
"""
viewed_by = models.ForeignKey(
AUTH_USER_MODEL,
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name="%(app_label)s_%(class)s_viewed_records",
verbose_name=_("viewed by")
)
viewed_dt = models.DateTimeField(
_("viewed date/time"),
blank=True,
help_text=_("Date and time the record was last viewed."),
null=True
)
class Meta:
abstract = True
[docs] def audit(self, user, commit=True):
"""Update viewed fields.
.. important::
This method makes calls to ``super()`` which may also trigger the update of other fields if the model also
extends :py:class:`AddedByModel` or :py:class:`ModifiedByModel`. See ``mark_viewed()`` if you just want to
update the ``viewed_by`` and ``viewed_dt`` fields.
"""
self.viewed_by = user
self.viewed_dt = now()
super().audit(user, commit=commit)
[docs] def mark_viewed(self, user, commit=True):
"""Mark the record as viewed.
:param user: The user that reviewed the record.
:type user: AUTH_USER_MODEL
:param commit: Indicates the record should be saved after updating the field.
:type commit: bool
"""
self.viewed_by = user
self.viewed_dt = now()
if commit:
self.save(update_fields=["viewed_by", "viewed_dt"])
[docs] def mark_unviewed(self, commit=True):
"""Mark the record as "un-viewed", removing the last viewed data.
:param commit: Indicates the record should be saved after updating the field.
:type commit: bool
"""
self.viewed_by = None
self.viewed_dt = None
if commit:
self.save(update_fields=["viewed_by", "viewed_dt"])
@property
def viewed_by_name(self):
"""Get the full name or user name of the user that last viewed the record.
:rtype: str
"""
return get_user_name(self.viewed_by)