Skip to content

Instantly share code, notes, and snippets.

@powderflask
Created December 19, 2023 04:55
Show Gist options
  • Save powderflask/f0ae8b9ae6c7bb471f591de5175204cd to your computer and use it in GitHub Desktop.
Save powderflask/f0ae8b9ae6c7bb471f591de5175204cd to your computer and use it in GitHub Desktop.
Django management command to wipe all traces of a django app with models from DB
"""
Completely remove all traces of a django app from the DB.
Suggested workflow:
Step 1: carefully remove dependencies on app from code base
Step 2: run this command to remove all traces of app model instances and content types from DB
Step 3: deploy code changes and this command everywhere (e.g., on stage and production codebase and DB)
At some future date...
Step 4: remove app from settings.INSTALLED_APPS
remove app code from codebase
Step 5: deploy new codebase everywhere
"""
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import Permission
from django.contrib.admin.models import LogEntry
from django.core.management import call_command
from django.core.management.base import BaseCommand
def confirm(question, default=None):
result = input("%s (y/n): " % question)
if not result and default is not None:
return default
while len(result) < 1 or result[0].lower() not in "yn":
result = input("Please answer yes or no: ")
return result[0].lower() == "y"
class Command(BaseCommand ):
help = "Completely wipe app from the DB."
def add_arguments(self, parser):
parser.add_argument('--quiet', action='store_true', help='suppress verbose output')
parser.add_argument('app_label', default=None, help='app label of app to remove.')
def instructions(self):
self.stdout.write("CAUTION: this command will completely remove all traces of the app from the DB.")
self.stdout.write(" Before starting...")
self.stdout.write(" - ensure all dependencies on the app have been removed from code base;")
self.stdout.write(" - carefully check for any remaining DB relations to the app models;")
self.stdout.write(" - check for and eliminate any migration dependencies on the app;")
self.stdout.write(" - otherwise, leave app installed and code intact (at least apps, models, and migrations);")
def handle(self, *args, **options):
app_label = options['app_label']
quiet = options.get('quiet', False)
if not quiet:
self.instructions()
if confirm(f'Proceed with removing {app_label}? ', default='n'):
self.remove_app(app_label, quiet)
elif not quiet:
self.stdout.write(" Operation aborted. No changes made.")
def remove_app(self, app_label, quiet=False):
""" Completely wipe all traces of app_label app from DB """
# pre-fetch all objects related to the app's content types
# perms will cascade, but delete them first so whole op. can be in DB
content_types = ContentType.objects.filter(app_label=app_label).all()
permissions = Permission.objects.filter(content_type_id__in=content_types)
log_entries = LogEntry.objects.filter(content_type_id__in=content_types)
if not quiet:
self.stdout.write(f'Removing all traces of {app_label}, including...')
self.stdout.write(f' {content_types.count()} ContentType :')
self.stdout.write(f' {[c.model for c in content_types]}')
self.stdout.write(f' {permissions.count()} Permission :')
self.stdout.write(f' {[p.codename for p in permissions]}')
self.stdout.write(f' {log_entries.count()} LogEntry :')
self.stdout.write(f' {[l.change_message for l in log_entries]}')
go = confirm(f'run `> migrate {app_label} zero`; and delete related content type data?', default='n')
if go:
# reverse migrations to remove model DB tables and migrations entries
call_command('migrate', app_label, 'zero')
# delete the content type related data
log_entries.delete()
permissions.delete()
content_types.delete()
if not quiet:
self.stdout.write(f'{app_label} successfully removed.')
self.stdout.write(f'Next steps: deploy this change everywhere; then remove app code at some future date.')
elif not quiet:
self.stdout.write(" Operation aborted. No changes made.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment