Skip to content

Model History


Model History is a core feature that is intended to be used to keep an audit history of ALL changes to a model. All Tenancy Models are to have a history model created for it.

Adding History to a Model

Most of the work has already been done, all that is required to add history to a model is the creation of the following:

  • Model

  • Function get_serialized_model added to audit model (parent model only)

  • Function get_serialized_child_model added to audit model (child model only)

  • (child model only) model name added to list attribute in core.models.model_history.ModelHistory.child_history_models

    Tip

    To obtain the model name you can use the api at endpoint api/v2/base/content_type and then filtering by app_name and model. The value in the model field is what's required.

Model

The model must inherit from core.models.model_history.ModelHistory. This model requires a models.ForeignKey field named model be added to the new history model that will be receiving the history feature. The foriegn key relationship is to the model that will be receiving the history. There is also a requirement to add a function called get_serialized_model that returns the serialized model, using the "base" serializer. The model that is serialized is the model the history is for,

As history can be broken up into parent-child relationships, if the model in question is a child model. The history model that will then be inherited from is the parent model history class. As this class will already contain the field model, the child history class must add field child_model which like the field model is also a models.ForeignKey.

Like the parent history model, A serializer is required. In this case the serializer function is to be called get_serialized_child_model that returns the serialized child model. Again like the parent history model using the "base" serializer. The model that is serialized is the child model the history is for,

Example model

This example is for adding history to the Device Model

models/device_history.py
from django.db import models

from core.models.model_history import ModelHistory

from itam.models.device import Device



class DeviceHistory(
    ModelHistory
):


    class Meta:

        db_table = 'itam_device_history'

        ordering = ModelHistory._meta.ordering

        verbose_name = 'Device History'

        verbose_name_plural = 'Device History'


    model = models.ForeignKey(
        Device,
        blank = False,
        help_text = 'Model this note belongs to',
        null = False,
        on_delete = models.CASCADE,
        related_name = 'history',
        verbose_name = 'Model',
    )

    table_fields: list = []

    page_layout: dict = []


    def get_object(self):

        return self


    def get_serialized_model(self, serializer_context):

        model = None

        from itam.serializers.device import DeviceBaseSerializer

        model = DeviceBaseSerializer(self.model, context = serializer_context)

        return model

This example is for adding history to the child model DeviceSoftware

models/device_software_history.py
from django.db import models

from itam.models.device import DeviceSoftware
from itam.models.device_history import (
    DeviceHistory
)



class DeviceSoftwareHistory(
    DeviceHistory
):


    class Meta:

        db_table = 'itam_devicesoftware_history'

        ordering = DeviceHistory._meta.ordering

        verbose_name = 'Device Software History'

        verbose_name_plural = 'Device Software History'


    child_model = models.ForeignKey(
        DeviceSoftware,
        blank = False,
        help_text = 'Model this note belongs to',
        null = True,
        on_delete = models.SET_NULL,
        related_name = 'history',
        verbose_name = 'Model',
    )

    table_fields: list = []

    page_layout: dict = []


    def get_serialized_child_model(self, serializer_context):

        model = None

        from itam.serializers.software import SoftwareBaseSerializer

        model = SoftwareBaseSerializer(self.child_model.software, context = serializer_context)

        return model

Tip

Take note of the inheritence and the fact that children don't override parent objects.

Requirement

Ensure that for the model and child_model fields that they are called with related_name = history. This ensures that ALL models that have history, will have an attribute called history that is available when working with that model.

Audit Model

For history to save, there is a requirement to add a function to the model being audited. This function is to be called save_history. The sole purpose of this function is to call the super class (History class) function of the same name, although this time passing the history model.

Example of the required function, in this case for the Device model.

def save_history(self, before: dict, after: dict) -> bool:

    from itam.models.device_history import DeviceHistory

    history = super().save_history(
        before = before,
        after = after,
        history_model = DeviceHistory
    )

    return history

Testing

As with any other object within Centurion, the addition of a feature requires it be tested. The following Test Suites are available:

  • Unit - API Fields checks

    • core.tests.abstract.test_unit_model_history_api_v2.PrimaryModelHistoryAPI for parent / Primary history model

    • core.tests.abstract.test_unit_model_history_api_v2.ChildModelHistoryAPI for child history model

  • Functional - History entry checks

    • core.tests.abstract.test_functional_history.HistoryEntriesCommon for parent / Primary history model

    • core.tests.abstract.test_functional_history.HistoryEntriesChildModel for child history model

About:

This page forms part of our Project Centurion ERP.

Page Metadata
Version: ToDo: place files short git commit here
Date Created: 2025-02-15
Date Edited: 2025-02-17

Contribution:

Would You like to contribute to our Centurion ERP project? You can assist in the following ways:

 

ToDo: Add the page list of contributors