diff --git a/apiserver/plane/app/views/issue/base.py b/apiserver/plane/app/views/issue/base.py index 47d02721a54..7d2d7a0d44d 100644 --- a/apiserver/plane/app/views/issue/base.py +++ b/apiserver/plane/app/views/issue/base.py @@ -166,6 +166,7 @@ def get(self, request, slug, project_id): "link_count", "is_draft", "archived_at", + "deleted_at", ) datetime_fields = ["created_at", "updated_at"] issues = user_timezone_converter( @@ -400,6 +401,7 @@ def create(self, request, slug, project_id): "link_count", "is_draft", "archived_at", + "deleted_at", ) .first() ) diff --git a/apiserver/plane/bgtasks/deletion_task.py b/apiserver/plane/bgtasks/deletion_task.py new file mode 100644 index 00000000000..6b155934e9a --- /dev/null +++ b/apiserver/plane/bgtasks/deletion_task.py @@ -0,0 +1,158 @@ +# Django imports +from django.utils import timezone +from django.apps import apps +from django.core.exceptions import ObjectDoesNotExist + +# Third party imports +from celery import shared_task + + +@shared_task +def soft_delete_related_objects( + app_label, model_name, instance_pk, using=None +): + model_class = apps.get_model(app_label, model_name) + instance = model_class.all_objects.get(pk=instance_pk) + related_fields = instance._meta.get_fields() + for field in related_fields: + if field.one_to_many or field.one_to_one or field.many_to_many: + try: + if field.one_to_many or field.many_to_many: + related_objects = getattr(instance, field.name).all() + elif field.one_to_one: + related_object = getattr(instance, field.name) + related_objects = ( + [related_object] if related_object is not None else [] + ) + for obj in related_objects: + if obj: + obj.deleted_at = timezone.now() + obj.save(using=using) + except ObjectDoesNotExist: + pass + + +# @shared_task +def restore_related_objects(app_label, model_name, instance_pk, using=None): + pass + + +@shared_task +def hard_delete(): + + from plane.db.models import ( + Workspace, + Project, + Cycle, + Module, + Issue, + Page, + IssueView, + Label, + State, + IssueActivity, + IssueComment, + IssueLink, + IssueReaction, + UserFavorite, + ModuleIssue, + CycleIssue, + Estimate, + EstimatePoint + ) + + # check delete workspace + _ = Workspace.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + # check delete project + _ = Project.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + # check delete cycle + _ = Cycle.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + # check delete module + _ = Module.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + # check delete issue + _ = Issue.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + # check delete page + _ = Page.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + # check delete view + _ = IssueView.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + # check delete label + _ = Label.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + # check delete state + _ = State.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + _ = IssueActivity.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + _ = IssueComment.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + _ = IssueLink.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + _ = IssueReaction.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + _ = UserFavorite.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + _ = ModuleIssue.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + _ = CycleIssue.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + _ = Estimate.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + _ = EstimatePoint.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + # at last, check for every thing which ever is left and delete it + # Get all Django models + all_models = apps.get_models() + + # Iterate through all models + for model in all_models: + # Check if the model has a 'deleted_at' field + if hasattr(model, "deleted_at"): + # Get all instances where 'deleted_at' is greater than 30 days ago + _ = model.all_objects.filter( + deleted_at__lt=timezone.now() - timezone.timedelta(days=30) + ).delete() + + return diff --git a/apiserver/plane/bgtasks/notification_task.py b/apiserver/plane/bgtasks/notification_task.py index 9dfd0c16dc8..0189316cf3c 100644 --- a/apiserver/plane/bgtasks/notification_task.py +++ b/apiserver/plane/bgtasks/notification_task.py @@ -221,7 +221,6 @@ def notifications( else None ) if type not in [ - "issue.activity.deleted", "cycle.activity.created", "cycle.activity.deleted", "module.activity.created", diff --git a/apiserver/plane/celery.py b/apiserver/plane/celery.py index d3e742f14a9..4d651255683 100644 --- a/apiserver/plane/celery.py +++ b/apiserver/plane/celery.py @@ -36,6 +36,10 @@ "task": "plane.bgtasks.api_logs_task.delete_api_logs", "schedule": crontab(hour=0, minute=0), }, + "check-every-day-to-delete-hard-delete": { + "task": "plane.bgtasks.deletion_task.hard_delete", + "schedule": crontab(hour=0, minute=0), + }, } # Load task modules from all registered Django app configs. diff --git a/apiserver/plane/db/migrations/0073_analyticview_deleted_at_apiactivitylog_deleted_at_and_more.py b/apiserver/plane/db/migrations/0073_analyticview_deleted_at_apiactivitylog_deleted_at_and_more.py new file mode 100644 index 00000000000..ba0e5307edc --- /dev/null +++ b/apiserver/plane/db/migrations/0073_analyticview_deleted_at_apiactivitylog_deleted_at_and_more.py @@ -0,0 +1,423 @@ +# Generated by Django 4.2.11 on 2024-07-26 11:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('db', '0072_issueattachment_external_id_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='analyticview', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='apiactivitylog', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='apitoken', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='commentreaction', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='cycle', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='cyclefavorite', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='cycleissue', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='cycleuserproperties', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='dashboard', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='dashboardwidget', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='deployboard', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='emailnotificationlog', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='estimate', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='estimatepoint', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='exporterhistory', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='fileasset', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='githubcommentsync', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='githubissuesync', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='githubrepository', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='githubrepositorysync', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='globalview', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='importer', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='inbox', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='inboxissue', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='integration', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issue', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issueactivity', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issueassignee', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issueattachment', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issueblocker', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issuecomment', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issuelabel', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issuelink', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issuemention', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issuereaction', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issuerelation', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issuesequence', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issuesubscriber', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issuetype', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issueuserproperty', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issueview', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issueviewfavorite', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='issuevote', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='label', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='module', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='modulefavorite', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='moduleissue', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='modulelink', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='modulemember', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='moduleuserproperties', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='notification', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='page', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='pageblock', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='pagefavorite', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='pagelabel', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='pagelog', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='pageversion', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='project', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='projectdeployboard', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='projectfavorite', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='projectidentifier', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='projectmember', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='projectmemberinvite', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='projectpage', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='projectpublicmember', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='slackprojectsync', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='socialloginconnection', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='state', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='team', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='teammember', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='teampage', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='userfavorite', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='usernotificationpreference', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='userrecentvisit', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='webhook', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='webhooklog', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='workspace', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='workspaceintegration', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='workspacemember', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='workspacememberinvite', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='workspacetheme', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='workspaceuserproperties', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + ] diff --git a/apiserver/plane/db/mixins.py b/apiserver/plane/db/mixins.py index f1756e5adca..0203eb8ce80 100644 --- a/apiserver/plane/db/mixins.py +++ b/apiserver/plane/db/mixins.py @@ -1,7 +1,9 @@ -# Python imports - # Django imports from django.db import models +from django.utils import timezone + +# Module imports +from plane.bgtasks.deletion_task import soft_delete_related_objects class TimeAuditModel(models.Model): @@ -41,7 +43,45 @@ class Meta: abstract = True -class AuditModel(TimeAuditModel, UserAuditModel): +class SoftDeletionManager(models.Manager): + def get_queryset(self): + return super().get_queryset().filter(deleted_at__isnull=True) + + +class SoftDeleteModel(models.Model): + """To soft delete records""" + + deleted_at = models.DateTimeField( + verbose_name="Deleted At", + null=True, + blank=True, + ) + + objects = SoftDeletionManager() + all_objects = models.Manager() + + class Meta: + abstract = True + + def delete(self, using=None, soft=True, *args, **kwargs): + if soft: + # Soft delete the current instance + self.deleted_at = timezone.now() + self.save(using=using) + + soft_delete_related_objects.delay( + self._meta.app_label, + self._meta.model_name, + self.pk, + using=using, + ) + + else: + # Perform hard delete if soft deletion is not enabled + return super().delete(using=using, *args, **kwargs) + + +class AuditModel(TimeAuditModel, UserAuditModel, SoftDeleteModel): """To path when the record was created and last modified""" class Meta: diff --git a/apiserver/plane/db/models/cycle.py b/apiserver/plane/db/models/cycle.py index 5128ecbc501..89c6e1707a6 100644 --- a/apiserver/plane/db/models/cycle.py +++ b/apiserver/plane/db/models/cycle.py @@ -116,6 +116,7 @@ def __str__(self): return f"{self.cycle}" +# DEPRECATED TODO: - Remove in next release class CycleFavorite(ProjectBaseModel): """_summary_ CycleFavorite (model): To store all the cycle favorite of the user diff --git a/apiserver/plane/db/models/issue.py b/apiserver/plane/db/models/issue.py index b4c2db9ff67..4dbaa71c27d 100644 --- a/apiserver/plane/db/models/issue.py +++ b/apiserver/plane/db/models/issue.py @@ -89,6 +89,7 @@ def get_queryset(self): | models.Q(issue_inbox__status=2) | models.Q(issue_inbox__isnull=True) ) + .filter(deleted_at__isnull=True) .filter(state__is_triage=False) .exclude(archived_at__isnull=False) .exclude(project__archived_at__isnull=False) diff --git a/apiserver/plane/db/models/module.py b/apiserver/plane/db/models/module.py index a6b55f246ff..694771f88b1 100644 --- a/apiserver/plane/db/models/module.py +++ b/apiserver/plane/db/models/module.py @@ -169,6 +169,7 @@ def __str__(self): return f"{self.module.name} {self.url}" +# DEPRECATED TODO: - Remove in next release class ModuleFavorite(ProjectBaseModel): """_summary_ ModuleFavorite (model): To store all the module favorite of the user diff --git a/apiserver/plane/db/models/page.py b/apiserver/plane/db/models/page.py index bc6f17b34a4..97381c2f65a 100644 --- a/apiserver/plane/db/models/page.py +++ b/apiserver/plane/db/models/page.py @@ -119,6 +119,7 @@ def __str__(self): return f"{self.page.name} {self.entity_name}" +# DEPRECATED TODO: - Remove in next release class PageBlock(ProjectBaseModel): page = models.ForeignKey( "db.Page", on_delete=models.CASCADE, related_name="blocks" @@ -175,6 +176,7 @@ def __str__(self): return f"{self.page.name} <{self.name}>" +# DEPRECATED TODO: - Remove in next release class PageFavorite(ProjectBaseModel): user = models.ForeignKey( settings.AUTH_USER_MODEL, diff --git a/apiserver/plane/db/models/project.py b/apiserver/plane/db/models/project.py index c9a8a34bc2b..9e04bb4c7d7 100644 --- a/apiserver/plane/db/models/project.py +++ b/apiserver/plane/db/models/project.py @@ -230,6 +230,7 @@ class Meta: ordering = ("-created_at",) +# DEPRECATED TODO: - Remove in next release class ProjectFavorite(ProjectBaseModel): user = models.ForeignKey( settings.AUTH_USER_MODEL, diff --git a/apiserver/plane/db/models/view.py b/apiserver/plane/db/models/view.py index 022c536ded5..2a5bae56919 100644 --- a/apiserver/plane/db/models/view.py +++ b/apiserver/plane/db/models/view.py @@ -52,7 +52,6 @@ def get_default_display_properties(): "updated_on": True, } - # DEPRECATED TODO: - Remove in next release class GlobalView(BaseModel): workspace = models.ForeignKey( @@ -142,6 +141,7 @@ def __str__(self): return f"{self.name} <{self.project.name}>" +# DEPRECATED TODO: - Remove in next release class IssueViewFavorite(ProjectBaseModel): user = models.ForeignKey( settings.AUTH_USER_MODEL, diff --git a/apiserver/plane/license/migrations/0004_changelog_deleted_at_instance_deleted_at_and_more.py b/apiserver/plane/license/migrations/0004_changelog_deleted_at_instance_deleted_at_and_more.py new file mode 100644 index 00000000000..4e238877c33 --- /dev/null +++ b/apiserver/plane/license/migrations/0004_changelog_deleted_at_instance_deleted_at_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.11 on 2024-07-26 11:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('license', '0003_alter_changelog_title_alter_changelog_version_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='changelog', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='instance', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='instanceadmin', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + migrations.AddField( + model_name='instanceconfiguration', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='Deleted At'), + ), + ]