[WEB-3744] Append the deleted_at timestamp to workspcace slug when it's soft deleted (#6862)

This commit is contained in:
Dheeraj Kumar Ketireddy 2025-04-02 23:07:26 +05:30 committed by GitHub
parent 3f652ba44e
commit 81fae36c23
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 110 additions and 1 deletions

View file

@ -0,0 +1,78 @@
import time
from django.core.management.base import BaseCommand
from django.db import transaction
from plane.db.models import Workspace
class Command(BaseCommand):
help = "Updates the slug of a soft-deleted workspace by appending the epoch timestamp"
def add_arguments(self, parser):
parser.add_argument(
"slug",
type=str,
help="The slug of the workspace to update",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Run the command without making any changes",
)
def handle(self, *args, **options):
slug = options["slug"]
dry_run = options["dry_run"]
# Get the workspace with the specified slug
try:
workspace = Workspace.all_objects.get(slug=slug)
except Workspace.DoesNotExist:
self.stdout.write(
self.style.ERROR(f"Workspace with slug '{slug}' not found.")
)
return
# Check if the workspace is soft-deleted
if workspace.deleted_at is None:
self.stdout.write(
self.style.WARNING(
f"Workspace '{workspace.name}' (slug: {workspace.slug}) is not deleted."
)
)
return
# Check if the slug already has a timestamp appended
if "__" in workspace.slug and workspace.slug.split("__")[-1].isdigit():
self.stdout.write(
self.style.WARNING(
f"Workspace '{workspace.name}' (slug: {workspace.slug}) already has a timestamp appended."
)
)
return
# Get the deletion timestamp
deletion_timestamp = int(workspace.deleted_at.timestamp())
# Create the new slug with the deletion timestamp
new_slug = f"{workspace.slug}__{deletion_timestamp}"
if dry_run:
self.stdout.write(
f"Would update workspace '{workspace.name}' slug from '{workspace.slug}' to '{new_slug}'"
)
else:
try:
with transaction.atomic():
workspace.slug = new_slug
workspace.save(update_fields=["slug"])
self.stdout.write(
self.style.SUCCESS(
f"Updated workspace '{workspace.name}' slug from '{workspace.slug}' to '{new_slug}'"
)
)
except Exception as e:
self.stdout.write(
self.style.ERROR(
f"Error updating workspace '{workspace.name}': {str(e)}"
)
)

View file

@ -1,6 +1,9 @@
# Python imports
from django.db.models.functions import Ln
import pytz
import time
from django.utils import timezone
from typing import Optional, Any, Tuple, Dict
# Django imports
from django.conf import settings
@ -149,6 +152,34 @@ class Workspace(BaseModel):
return self.logo
return None
def delete(
self,
using: Optional[str] = None,
soft: bool = True,
*args: Any,
**kwargs: Any
):
"""
Override the delete method to append epoch timestamp to the slug when soft deleting.
Args:
using: The database alias to use for the deletion.
soft: Whether to perform a soft delete (True) or hard delete (False).
*args: Additional positional arguments.
**kwargs: Additional keyword arguments.
"""
# Call the parent class's delete method first
result = super().delete(using=using, soft=soft, *args, **kwargs)
# If it's a soft delete and the model still exists (not hard deleted)
if soft and hasattr(self, 'deleted_at') and self.deleted_at:
# Use the deleted_at timestamp to update the slug
deletion_timestamp: int = int(self.deleted_at.timestamp())
self.slug = f"{self.slug}__{deletion_timestamp}"
self.save(update_fields=["slug"])
return result
class Meta:
verbose_name = "Workspace"
verbose_name_plural = "Workspaces"
@ -391,7 +422,7 @@ class WorkspaceHomePreference(BaseModel):
class WorkspaceUserPreference(BaseModel):
"""Preference for the workspace for a user"""
class UserPreferenceKeys(models.TextChoices):
class UserPreferenceKeys(models.TextChoices):
VIEWS = "views", "Views"
ACTIVE_CYCLES = "active_cycles", "Active Cycles"
ANALYTICS = "analytics", "Analytics"