docs(development): Partial update to models

ref: #772 #767
This commit is contained in:
2025-05-25 04:05:06 +09:30
parent b5147e2448
commit da3a471d04

View File

@ -3,27 +3,33 @@ title: Models
description: Centurion ERP Models development documentation
date: 2024-07-14
template: project.html
about: https://gitlab.com/nofusscomputing/infrastructure/configuration-management/centurion_erp
about: https://github.com/nofusscomputing/centurion_erp
---
Models within Centurion ERP are how the data is structured within the database. This page contains documentation pertinent to the development of the models for use with Centurion ERP.
All models within Centurion ERP are what we call a "Tenancy Object." A tenancy object is a model that takes advantage of the multi-tenancy features of Centurion ERP.
## Creating a Model
All models are class based models. Due to this fact, most of the work has been done for the models. We have two types of models: normal and sub-model. In most cases to create a model you will only need to declare the fields and any validation.
When designing or even implementing a model do consider that what is defined within its class must take into account that the model is responsible for ensure that no data is saved to the database wherein it could create an inconsistancy. For ensureing this does not see [Validation](#validation-methods) below.
## Requirements
### Requirements
All models must meet the following requirements:
When creating models they must meet the following requirements:
- inherits from `app.access.models.TenancyObject`
- inherits from `core.models.centurion.CenturionModel` or if a submodel `core.models.centurion.CenturionSubModel` and its base model.
!!! tip
There is no requirement to include class [`app.core.mixin.history_save.SaveHistory`](./api/models/core_history_save.md) for inheritence as this class is already included within class `app.access.models.TenancyObject`.
- class has the folloing objects defined:
!!! note
If there is a specific use case for the object not to be a tenancy object, this will need to be discussed with a maintainer.
- Function `__str__` is defined to return the models name when requested as a string.
- Attribute `page_layout` is defined with the models UI page layout
- Attribute `table_fields` is defined with the fields to display by default for viewing the model within a table.
- class has `__str__` method defined to return that is used to return a default value if no field is specified.
- Fields are initialized with the following parameters:
@ -31,13 +37,6 @@ All models must meet the following requirements:
- `help_text`
- No `queryset` is to return data that the user has not got access to.
- Single Field validation is conducted if required.
!!! danger "Requirement"
Multi-field validation, or validation that requires access to multiple fields must be done within the [form class](./forms.md#requirements).
- contains a `Meta` sub-class with following attributes:
- `ordering` _Order the results are returned in._
@ -46,82 +45,91 @@ All models must meet the following requirements:
- `verbose_name_plural` _Plural Name of the model_
- If creating a new model, function `access.functions.permissions.permission_queryset()` has been updated to display the models permission(s)
- No `queryset` is to return data that the user has not got access to.
- Attribute `page_layout` is defined with the models UI page layout
- Attribute `table_fields` is defined with the fields to display by default for viewing the model within a table.
- `clean()` method within a model is **only** used to ensure that the data entered into the DB is valid and/or to ensure application wide changes/validation is conducted prior to saving model.
- Tenancy models must have the ability to have a [knowledge base article](#knowledge-base-article-linking) linked to it.
- Models must save audit history
!!! tip
It's a good idea to create the initial model class, then create and add the model tests for that class. This way you can run the tests to ensure that the requirements are met. Of Note, the tests may not cover ALL of the requirements section, due diligence will need to be exercised.
## Creating a Model
### Normal (Base) Model
Within Centurion ERP there are two types of models, they are:
This model is nothing special and is the core model type that will be created. If this model is inherited by a sub-model its also referred to as a _"base model."_ Creating one is as simple as simple as declaring the class, inheriting from `core.models.centurion.CenturionModel` then adding your fields and [validation method(s)](#validation-methods).
- Standard
- Sub-Model
#### Available Base models
The are already some base models within Centurion ERP that you can use to extend and/or add features to Centurion easier. They are:
Within Centurion ERP there are some base models that are designed to be used for extending Centurion. They are:
- [Asset](./accounting/asset.md)
- [ITAM Asset](./itam/it_asset.md)
_A sub-model of Asset. Used by the ITAM module_
- [Entity](./core/entity.md)
Using a base model reduces the effort required to add a feature. This is such due to how the base model / sub-model has been designed. That is the base model has the core features so you don't need to add them. All that is required for the extension is that you add your fields and test the differences.
- Company
_A sub-model of Entity. All business like entities_
### Standard Model
- Person
This is your typical model that you would define within any Django Application. This includes Abstract models and precludes multi-table inherited models.
_A sub-model of Entity. As the name implies, A person_
- Contact
_A sub-model of Person. Contains fields that are common to a contact_
- [Ticket](./core/ticket.md)
_All Ticketing Models use this_
- [Ticket Comment](./core/ticket_comment.md)
_All Ticket Comment Models use this_
All sub-models are intended to be extended and contain the core features for ALL models. This aids in extensibility and reduces the work required to add a model.
!!! tip
Core features can be switched on and/or off for the model you are creating. Before doing so though, consider the reasons for doing so and do discuss with a maintainer. This discussion will be required to ensure that you are not unintentially removing a feature that is a **must** for a Centurion ERP model.
### Sub-Model
This model is known within Django as multi-table inherited models. That is where the base model is a concrete class (not an Abstract model) and the super model inherits from the concrete base model. In this instance both models get their own database tables.
A Sub-model specifically inherits from a normal model with the purpose of using the base model fields (the common fields) and if required specifying its own fields. A sub-model provides an additional feature in that the data from the base model can now be based off of the permissions of the sub-model, not the base. THis model inherits from `core.models.centurion.CenturionSubModel` and its base model.
#### Available Sub-Models
## Core Features
We do have some core sub-models available. There intended purpose is to serve as a single place for all items of that type. Available sub-models are:
All models must contain the core features, being:
- [Asset](./accounting/asset.md)
- [Audit History](./core/model_history.md)
- [IT Asset](./itam/it_asset.md)
_Every change that occurs to a model is recorded_
- [Ticket](./core/ticket.md)
- [Notes](./core/model_notes.md)
- [Ticket Comment](./core/ticket_comment.md)
_Adds the ability for a user to add comments to a model_
All sub-models are intended to be extended and contain the core features for ALL models. This aids in extensibility and reduces the work required to add a model.
- KB Article linking
_Enables linking a knowledge base article to a model_
- Model Tag
_Enables the possibility within markdown fields to use its [tag](../user/core/markdown.md#model-reference--model-tag) to create a link to the model._
<!-- markdownlint-disable -->
#### Requirements
<!-- markdownlint-restore -->
## Validation Methods
- Must **not** be an abstract class
Within All of our models including when they are created via an API serializer, the models [validators](https://docs.djangoproject.com/en/5.1/ref/models/instances/#validating-objects) are called. The models validators are responsible for ensuring that no data goes into the database that may create an inconsistancy.
- Attribute `sub_model_type` must be specified within the models `Meta` sub-class
!!! example
You have defined a model that has a user field that must always have a value. This model can be access via the API, in which the user field is auto-populated by object `request.user`. In the same token you have admin commands that uses this model.
Now every time you use the admin command to create this model, it will fail validation due to there being no value for the user field. This is where the models validator methods come into play. defining method `clean()` within the model with the logic required to ensure that the user field has value for the user field ensures that the model now when used by the admin command is consistant and meets the intent of the models purpose.
- File name is `<base model>_<sub_model_type>` where `base model` is the value of `Meta.sub_model_type` or the first model in the chain.
## Checklist
This section details the additional items that may need to be done when adding a new model:
- If the model is a primary model, add it to model reference rendering in `app/core/lib/markdown_plugins/model_reference.py` function `tag_html`
- If the model is a primary model, add it to the model link slash command in `app/core/lib/slash_commands/linked_model.py` function `command_linked_model`
!!! tip
It's a good idea to create the initial model class, then create and add the model tests for that class. This way you can run the tests to ensure that the requirements are met. Of Note, the tests may not cover ALL of the requirements section, due diligence will need to be exercised.
Whilst most data that will use a model will be via an API Serializer, which in turn has its own validation. The models Validation is only to serve the purpose of ensuring data consistancy.
## page_layout Attribute
@ -203,6 +211,80 @@ table_fields: list = [
```
- ToDo
- Avoid:
- adding `model.delete()` method
- adding `model.save()` method
- Do
- Add `model.clean()` To set/modify any field values, _if required_
## Requirements
- `clean()` method within a model is **only** used to ensure that the data entered into the DB is valid and/or to ensure application wide changes/validation is conducted prior to saving model.
- Tenancy models must have the ability to have a [knowledge base article](#knowledge-base-article-linking) linked to it.
- Models must save audit history
<!-- markdownlint-disable -->
#### Requirements
<!-- markdownlint-restore -->
- Must **not** be an abstract class
- Attribute `sub_model_type` must be specified within the models `Meta` sub-class
- File name is `<base model>_<sub_model_type>` where `base model` is the value of `Meta.sub_model_type` or the first model in the chain.
## Checklist
This section details the additional items that may need to be done when adding a new model:
- If the model is a primary model, add it to model reference rendering in `app/core/lib/markdown_plugins/model_reference.py` function `tag_html`
- If the model is a primary model, add it to the model link slash command in `app/core/lib/slash_commands/linked_model.py` function `command_linked_model`
## History
Adding [History](./core/model_history.md) to a model is automatic. If there is a desire not to have model history it can be disabled by adding attribute `_audit_enabled` to the model class and setting its value to `False.`
@ -270,20 +352,3 @@ All Tenancy Models must have the ability to be able to have a knowledge base art
]
```
## Docs to clean up
!!! note
The below documentation is still to be developed. As such what is written below may be incorrect.
for items that have a parent item, modification will need to be made to the mixin by adding the relevant check and setting the relevant keys.
``` python
if self._meta.model_name == 'deviceoperatingsystem':
item_parent_pk = self.device.pk
item_parent_class = self.device._meta.model_name
```