From 5186b2f1737a4efc57c34d154756c70a6fbff619 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 6 Jun 2025 11:14:45 +0930 Subject: [PATCH] feat(access): switch model Team to inheirt from CenturionModel ref: #789 #791 --- ...ove_team_is_global_model_notes_and_more.py | 110 ++++++++++++++++++ app/access/models/__init__.py | 2 - app/access/models/team.py | 101 ++-------------- app/access/serializers/centurionaudit_team.py | 56 +++++++++ .../serializers/centurionmodelnote_team.py | 87 ++++++++++++++ app/access/urls_api.py | 6 +- app/access/viewsets/team.py | 1 + 7 files changed, 268 insertions(+), 95 deletions(-) create mode 100644 app/access/migrations/0011_remove_team_is_global_model_notes_and_more.py create mode 100644 app/access/serializers/centurionaudit_team.py create mode 100644 app/access/serializers/centurionmodelnote_team.py diff --git a/app/access/migrations/0011_remove_team_is_global_model_notes_and_more.py b/app/access/migrations/0011_remove_team_is_global_model_notes_and_more.py new file mode 100644 index 00000000..ef84de87 --- /dev/null +++ b/app/access/migrations/0011_remove_team_is_global_model_notes_and_more.py @@ -0,0 +1,110 @@ +# Generated by Django 5.1.9 on 2025-06-06 01:41 + +import access.models.tenancy_abstract +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("access", "0010_company_alter_entity_entity_type_alter_person_dob_and_more"), + ("core", "0028_delete_history"), + ] + + operations = [ + migrations.RemoveField( + model_name="team", + name="is_global", + ), + migrations.AlterField( + model_name="team", + name="model_notes", + field=models.TextField( + blank=True, + help_text="Tid bits of information", + null=True, + verbose_name="Notes", + ), + ), + migrations.AlterField( + model_name="team", + name="organization", + field=models.ForeignKey( + help_text="Tenant this belongs to", + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to="access.tenant", + validators=[ + access.models.tenancy_abstract.TenancyAbstractModel.validatate_organization_exists + ], + verbose_name="Tenant", + ), + ), + migrations.CreateModel( + name="TeamAuditHistory", + fields=[ + ( + "centurionaudit_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="core.centurionaudit", + ), + ), + ( + "model", + models.ForeignKey( + help_text="Model this history belongs to", + on_delete=django.db.models.deletion.CASCADE, + related_name="audit_history", + to="access.team", + verbose_name="Model", + ), + ), + ], + options={ + "verbose_name": "Team History", + "verbose_name_plural": "Team Histories", + "db_table": "access_team_audithistory", + "managed": True, + }, + bases=("core.centurionaudit", models.Model), + ), + migrations.CreateModel( + name="TeamCenturionModelNote", + fields=[ + ( + "centurionmodelnote_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="core.centurionmodelnote", + ), + ), + ( + "model", + models.ForeignKey( + help_text="Model this note belongs to", + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to="access.team", + verbose_name="Model", + ), + ), + ], + options={ + "verbose_name": "Team Note", + "verbose_name_plural": "Team Notes", + "db_table": "access_team_centurionmodelnote", + "managed": True, + }, + bases=("core.centurionmodelnote", models.Model), + ) + ] diff --git a/app/access/models/__init__.py b/app/access/models/__init__.py index f4c13f0d..8c696e78 100644 --- a/app/access/models/__init__.py +++ b/app/access/models/__init__.py @@ -7,9 +7,7 @@ from . import role # pylint: disable=W0611:unused-import from .entity_history import EntityHistory # pylint: disable=W0611:unused-import from .organization_history import OrganizationHistory # pylint: disable=W0611:unused-import from .role_history import RoleHistory # pylint: disable=W0611:unused-import -from .team_history import TeamHistory # pylint: disable=W0611:unused-import from .entity_notes import EntityNotes # pylint: disable=W0611:unused-import from .organization_notes import OrganizationNotes # pylint: disable=W0611:unused-import from .role_notes import RoleNotes # pylint: disable=W0611:unused-import -from .team_notes import TeamNotes # pylint: disable=W0611:unused-import \ No newline at end of file diff --git a/app/access/models/team.py b/app/access/models/team.py index 25142abd..8b7e623d 100644 --- a/app/access/models/team.py +++ b/app/access/models/team.py @@ -1,20 +1,19 @@ from django.db import models from django.contrib.auth.models import Group -from rest_framework.reverse import reverse - from access.fields import ( - AutoCreatedField, AutoLastModifiedField ) -from access.models.tenant import Tenant -from access.models.tenancy import TenancyObject - -from core import exceptions as centurion_exceptions +from core.models.centurion import CenturionModel -class Team(Group, TenancyObject): + +class Team( + Group, + CenturionModel, +): + class Meta: @@ -25,27 +24,6 @@ class Team(Group, TenancyObject): verbose_name_plural = "Teams" - def save(self, force_insert=False, force_update=False, using=None, update_fields=None): - - if self.organization_id: - - self.name = self.organization.name.lower().replace(' ', '_') + '_' + self.team_name.lower().replace(' ', '_') - - super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields) - - - def validatate_organization_exists(self): - """Ensure that the user did provide an organization - - Raises: - ValidationError: User failed to supply organization. - """ - - if not self: - raise centurion_exceptions.ValidationError('You must provide an organization') - - - team_name = models.CharField( blank = False, help_text = 'Name to give this team', @@ -54,18 +32,6 @@ class Team(Group, TenancyObject): verbose_name = 'Name', ) - organization = models.ForeignKey( - Tenant, - blank = False, - help_text = 'Tenant this belongs to', - null = False, - on_delete = models.CASCADE, - validators = [validatate_organization_exists], - verbose_name = 'Tenant' - ) - - created = AutoCreatedField() - modified = AutoLastModifiedField() page_layout: dict = [ @@ -116,47 +82,16 @@ class Team(Group, TenancyObject): ] - def get_url( self, request = None ) -> str: - if request: + def clean_fields(self, exclude = None): - return reverse(f"v2:_api_v2_organization_team-detail", request=request, kwargs = self.get_url_kwargs() ) + if self.organization_id: - return reverse(f"v2:_api_v2_organization_team-detail", kwargs = self.get_url_kwargs() ) + self.name = self.organization.name.lower().replace(' ', '_') + '_' + self.team_name.lower().replace(' ', '_') - def get_url_kwargs(self) -> dict: - """Fetch the URL kwargs + super().clean_fields(exclude = exclude) - Returns: - dict: kwargs required for generating the URL with `reverse` - """ - - return { - 'organization_id': self.organization.id, - 'pk': self.id - } - - - def get_url_kwargs_notes(self) -> dict: - """Fetch the URL kwargs for model notes - - Returns: - dict: notes kwargs required for generating the URL with `reverse` - """ - - return { - 'organization_id': self.organization.id, - 'model_id': self.id - } - - - - # @property - # def parent_object(self): - # """ Fetch the parent object """ - - # return self.organization def permission_list(self) -> list: @@ -175,17 +110,3 @@ class Team(Group, TenancyObject): def __str__(self): return self.organization.name + ', ' + self.team_name - - - def save_history(self, before: dict, after: dict) -> bool: - - from access.models.team_history import TeamHistory - - history = super().save_history( - before = before, - after = after, - history_model = TeamHistory - ) - - - return history diff --git a/app/access/serializers/centurionaudit_team.py b/app/access/serializers/centurionaudit_team.py new file mode 100644 index 00000000..8835ca77 --- /dev/null +++ b/app/access/serializers/centurionaudit_team.py @@ -0,0 +1,56 @@ +from rest_framework import serializers + +from drf_spectacular.utils import extend_schema_serializer + +from api.serializers import common + +from centurion.models.meta import TeamAuditHistory # pylint: disable=E0401:import-error disable=E0611:no-name-in-module + +from core.serializers.centurionaudit import ( + BaseSerializer, + ViewSerializer as AuditHistoryViewSerializer +) + + + + +@extend_schema_serializer(component_name = 'TeamAuditHistoryModelSerializer') +class ModelSerializer( + common.CommonModelSerializer, + BaseSerializer +): + """Git Group Audit History Base Model""" + + + _urls = serializers.SerializerMethodField('get_url') + + + class Meta: + + model = TeamAuditHistory + + fields = [ + 'id', + 'organization', + 'display_name', + 'content_type', + 'model', + 'before', + 'after', + 'action', + 'user', + 'created', + '_urls', + ] + + read_only_fields = fields + + + +@extend_schema_serializer(component_name = 'TeamAuditHistoryViewSerializer') +class ViewSerializer( + ModelSerializer, + AuditHistoryViewSerializer, +): + """Git Group Audit History Base View Model""" + pass diff --git a/app/access/serializers/centurionmodelnote_team.py b/app/access/serializers/centurionmodelnote_team.py new file mode 100644 index 00000000..461733b4 --- /dev/null +++ b/app/access/serializers/centurionmodelnote_team.py @@ -0,0 +1,87 @@ +from rest_framework import serializers + +from drf_spectacular.utils import extend_schema_serializer + +from access.serializers.organization import (TenantBaseSerializer) + +from centurion.models.meta import TeamCenturionModelNote # pylint: disable=E0401:import-error disable=E0611:no-name-in-module + +from core.serializers.centurionmodelnote import ( # pylint: disable=W0611:unused-import + BaseSerializer, + ModelSerializer as BaseModelModelSerializer, + ViewSerializer as BaseModelViewSerializer +) + + + +@extend_schema_serializer(component_name = 'TeamModelNoteModelSerializer') +class ModelSerializer( + BaseModelModelSerializer, +): + + + _urls = serializers.SerializerMethodField('get_url') + + def get_url(self, item) -> dict: + + return { + '_self': item.get_url( request = self._context['view'].request ), + } + + + class Meta: + + model = TeamCenturionModelNote + + fields = [ + 'id', + 'organization', + 'display_name', + 'body', + 'created_by', + 'modified_by', + 'content_type', + 'model', + 'created', + 'modified', + '_urls', + ] + + read_only_fields = [ + 'id', + 'display_name', + 'organization', + 'created_by', + 'modified_by', + 'content_type', + 'model', + 'created', + 'modified', + '_urls', + ] + + + + def validate(self, attrs): + + is_valid = False + + note_model = self.Meta.model.model.field.related_model + + attrs['model'] = note_model.objects.get( + id = int( self.context['view'].kwargs['model_id'] ) + ) + + + is_valid = super().validate(attrs) + + return is_valid + + +@extend_schema_serializer(component_name = 'TeamModelNoteViewSerializer') +class ViewSerializer( + ModelSerializer, + BaseModelViewSerializer, +): + + organization = TenantBaseSerializer( many = False, read_only = True ) diff --git a/app/access/urls_api.py b/app/access/urls_api.py index 07bfc699..62601744 100644 --- a/app/access/urls_api.py +++ b/app/access/urls_api.py @@ -7,7 +7,7 @@ from access.viewsets import ( index as access_v2, organization as organization_v2, role, - team as team_v2, + team, team_user as team_user_v2 ) @@ -64,8 +64,8 @@ router.register( # ) router.register( - prefix = 'tenant/(?P[0-9]+)/team', viewset = team_v2.ViewSet, - basename = '_api_v2_organization_team' + prefix = 'tenant/(?P[0-9]+)/team', viewset = team.ViewSet, + basename = '_api_team' ) # router.register( diff --git a/app/access/viewsets/team.py b/app/access/viewsets/team.py index 6614bd9a..3318a970 100644 --- a/app/access/viewsets/team.py +++ b/app/access/viewsets/team.py @@ -3,6 +3,7 @@ from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiPara from access.models.tenant import Tenant as Organization # THis import only exists so that the migrations can be created from access.models.team_history import TeamHistory # pylint: disable=W0611:unused-import +from access.models.team_notes import TeamNotes # pylint: disable=W0611:unused-import from access.serializers.teams import ( # pylint: disable=W0611:unused-import Team, TeamModelSerializer,