How to write an elegant bulk delete Django management command that supports chunking with yaspin

How to write an elegant bulk delete Django management command that supports chunking with yaspin
Photo by Kent Pilcher / Unsplash

When developing with Django, efficiently managing bulk data operations is crucial for maintaining performance and ensuring a smooth user experience. A particularly challenging aspect involves creating a robust bulk delete command. This task becomes even more complex when dealing with large datasets, where deleting records in chunks is necessary to avoid overwhelming the database.

This blog post will guide you through the process of crafting an elegant bulk delete management command in Django, enhanced with the functionality of chunking.

We'll also integrate yaspin, a Python library providing a spin loader, to add a touch of interactivity and feedback during the command's execution. Whether you're a seasoned Django developer or just starting out, this tutorial will equip you with the skills to handle bulk deletes efficiently and gracefully in your Django projects.

appname/management/commands/bulk_delete.py
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction

from yaspin import yaspin

from notifi.models import PushNotification  # Adjust with your model

import logging

logger = logging.getLogger(__name__)

class Command(BaseCommand):
    help = 'Bulk delete old push notifications'

    def add_arguments(self, parser):
        parser.add_argument('--chunk_size', type=int, default=5000)

    def handle(self, *args, **options):
        chunk_size = options['chunk_size']

        try:
            queryset = PushNotification.objects.filter()
            total = queryset.count()

            if total == 0:
                logger.info("No data found to delete.")
                return

            with yaspin(text=f"Deleting {total} rows of PushNotifications", color="yellow") as spinner:
                while queryset.exists():
                    with transaction.atomic():
                        ids = queryset.values_list('id', flat=True)[:chunk_size]
                        deleted, _ = PushNotification.objects.filter(id__in=list(ids)).delete()
                        spinner.write(f"> Deleted {deleted} PushNotification records")
                spinner.ok("✅ Finished deleting.")

        except Exception as e:
            raise CommandError(f'Error during deletion: {e}')

Hope this helps you understand how to structure a management command properly for bulk deletion of large datasets.

Happy coding!