Source code for common.base_model

'''
Healthy Meals Web Site
Copyright (C) 2025 David A. Taylor of Taylored Web Sites (tayloredwebsites.com)
Licensed under AGPL-3.0-only.  See https://opensource.org/license/agpl-v3/

https://github.com/tayloredwebsites/healthy-meals - healthy_meals/base_model.py
'''

from django.db import models
from safedelete.models import SafeDeleteModel
from safedelete.models import SOFT_DELETE_CASCADE
from safedelete.managers import SafeDeleteManager
from auditlog.registry import auditlog
from auditlog.models import AuditlogHistoryField
from django.utils import timezone


[docs] class BaseModel(SafeDeleteModel): """ BaseModel is abstract class to base all models in this project - has soft delete functionality included through django-safedelete - https://django-safedelete.readthedocs.io/en/latest/index.html - has record history / versioning through django-auditlog - https://github.com/jazzband/django-auditlog SOFT DELETE FUNCTIONALITY Note: The customized functions for soft deletion are only found in model manager classes Thus??: to use the methods found in 'objects', their models must declare their custom manager based off of SafeDeleteManager (This should be validated!!!) See: accounts/models.py for an example - all_with_deleted() # Show all model records including the soft deleted models. - deleted_only() # Only show the soft deleted model records. - all(**kwargs) -> django.db.models.query.QuerySet # Show deleted model records. (default: {None}) - update_or_create(defaults=None, **kwargs) -> Tuple[django.db.models.base.Model, bool] # https://django-safedelete.readthedocs.io/en/latest/managers.html#safedelete.managers.SafeDeleteManager.update_or_create AUDITLOG VERSIONING HISTORY FUNCTIONALITY - has record history / versioning through django-auditlog (https://github.com/jazzband/django-auditlog) - this provides the ability to see all of the changes to fields (except fields excluded when registered in the model) - see: https://django-auditlog.readthedocs.io/en/latest/usage.html Note: To register auditlog to automatically log all changes to a model, it must be registered in the model To register auditlog, the last line of the model should have the auditlog.register statement. For Example (as can be seen in accounts/models.py): auditlog.register(CustomUser, exclude_fields=[ 'password', # protect this field for security reasons 'last_login', # do not update audit log for each login ] """ created_at = models.DateTimeField(default=timezone.now) updated_at = models.DateTimeField(auto_now=True) history = AuditlogHistoryField() # audit log to maintain record history _safedelete_policy = SOFT_DELETE_CASCADE # cascade soft deletes of records as well as child records. class Meta: abstract = True
[docs] def rec_history_count(self): '''Return the count of all of the history records for this user.''' return self.history.all().count()
[docs] def rec_history_field_was(self, user_rec, field_name): '''Return a dictionary of the previous values for this field, for this record.''' rec = self.history.all()[user_rec] return self.__get_field_changes(rec, field_name)[0]
[docs] def rec_history_field_is_now(self, user_rec, field_name): '''Return the latest history record value for this field (should be identical to current field value)''' rec = self.history.all()[user_rec] return self.__get_field_changes(rec, field_name)[1]
[docs] def rec_history_field_changed(self, user_rec, field_name): '''Return the number of records that are maintained in CustomUser's history table.''' rec = self.history.all()[user_rec] changes = self.__get_field_changes(rec, field_name) # print(f'changes: {changes}') return changes[0] != changes[1]
def __get_field_changes(self, hist_rec, field_name): '''Return a dictionary of the history for this record's field values.''' try: changes = hist_rec.changes_dict[field_name] # print(f'changes: {changes}') return changes except KeyError as e: # there was no change, audit log does not log values that do not change, so return array of None strings print(f'expected key error auditlog - no changes for field: {e}') return ['None', 'None']