""" Django management command to delete a user while preserving their submissions. Usage: uv run manage.py delete_user uv run manage.py delete_user --user-id uv run manage.py delete_user --dry-run """ from django.core.management.base import BaseCommand, CommandError from apps.accounts.models import User from apps.accounts.services import UserDeletionService class Command(BaseCommand): help = "Delete a user while preserving all their submissions" def add_arguments(self, parser): parser.add_argument( "username", nargs="?", type=str, help="Username of the user to delete" ) parser.add_argument( "--user-id", type=str, help="User ID of the user to delete (alternative to username)", ) parser.add_argument( "--dry-run", action="store_true", help="Show what would be deleted without actually deleting", ) parser.add_argument( "--force", action="store_true", help="Skip confirmation prompt" ) def handle(self, *args, **options): username = options.get("username") user_id = options.get("user_id") dry_run = options.get("dry_run", False) force = options.get("force", False) # Validate arguments if not username and not user_id: raise CommandError("You must provide either a username or --user-id") if username and user_id: raise CommandError("You cannot provide both username and --user-id") # Find the user try: if username: user = User.objects.get(username=username) else: user = User.objects.get(user_id=user_id) except User.DoesNotExist: identifier = username or user_id raise CommandError(f'User "{identifier}" does not exist') # Check if user can be deleted can_delete, reason = UserDeletionService.can_delete_user(user) if not can_delete: raise CommandError(f"Cannot delete user: {reason}") # Count submissions submission_counts = { "park_reviews": getattr( user, "park_reviews", user.__class__.objects.none() ).count(), "ride_reviews": getattr( user, "ride_reviews", user.__class__.objects.none() ).count(), "uploaded_park_photos": getattr( user, "uploaded_park_photos", user.__class__.objects.none() ).count(), "uploaded_ride_photos": getattr( user, "uploaded_ride_photos", user.__class__.objects.none() ).count(), "top_lists": getattr( user, "top_lists", user.__class__.objects.none() ).count(), "edit_submissions": getattr( user, "edit_submissions", user.__class__.objects.none() ).count(), "photo_submissions": getattr( user, "photo_submissions", user.__class__.objects.none() ).count(), } total_submissions = sum(submission_counts.values()) # Display user information self.stdout.write(self.style.WARNING("\nUser Information:")) self.stdout.write(f" Username: {user.username}") self.stdout.write(f" User ID: {user.user_id}") self.stdout.write(f" Email: {user.email}") self.stdout.write(f" Date Joined: {user.date_joined}") self.stdout.write(f" Role: {user.role}") # Display submission counts self.stdout.write(self.style.WARNING("\nSubmissions to preserve:")) for submission_type, count in submission_counts.items(): if count > 0: self.stdout.write( f' {submission_type.replace("_", " ").title()}: {count}' ) self.stdout.write(f"\nTotal submissions: {total_submissions}") if total_submissions > 0: self.stdout.write( self.style.SUCCESS( f'\nAll {total_submissions} submissions will be transferred to the "deleted_user" placeholder.' ) ) else: self.stdout.write( self.style.WARNING("\nNo submissions found for this user.") ) if dry_run: self.stdout.write(self.style.SUCCESS("\n[DRY RUN] No changes were made.")) return # Confirmation prompt if not force: self.stdout.write( self.style.WARNING( f'\nThis will permanently delete the user "{user.username}" ' f"but preserve all {total_submissions} submissions." ) ) confirm = input("Are you sure you want to continue? (yes/no): ") if confirm.lower() not in ["yes", "y"]: self.stdout.write(self.style.ERROR("Operation cancelled.")) return # Perform the deletion try: result = UserDeletionService.delete_user_preserve_submissions(user) self.stdout.write( self.style.SUCCESS( f'\nSuccessfully deleted user "{result["deleted_user"]["username"]}"' ) ) preserved_count = sum(result["preserved_submissions"].values()) if preserved_count > 0: self.stdout.write( self.style.SUCCESS( f'Preserved {preserved_count} submissions under user "{result["transferred_to"]["username"]}"' ) ) # Show detailed preservation summary self.stdout.write(self.style.WARNING("\nPreservation Summary:")) for submission_type, count in result["preserved_submissions"].items(): if count > 0: self.stdout.write( f' {submission_type.replace("_", " ").title()}: {count}' ) except Exception as e: raise CommandError(f"Error deleting user: {str(e)}")