Add: prototype cleanup_media command

This commit is contained in:
Stepan Zhukovsky 2023-09-14 01:19:05 +09:00
parent a7a85b1816
commit 431541b3cb
3 changed files with 59 additions and 0 deletions

View File

@ -0,0 +1,59 @@
import os
from django.core.management.base import BaseCommand
from django.apps import apps
from django.db.models import Q
from django.conf import settings
from django.db.models import FileField
class Command(BaseCommand):
# HELP MESSAGE:
help_part1 = 'This command deletes all media files from'
help_part2 = 'the MEDIA_ROOT directory which are no longer referenced'
help_part3 = 'by any of the models from installed_apps'
help = f'{help_part1} {help_part2} {help_part3}'
def handle(self, *args, **options):
all_models = apps.get_models()
physical_files = set()
db_files = set()
# Get all files from the database
for model in all_models:
file_fields = []
filters = Q()
for f_ in model._meta.fields:
if isinstance(f_, FileField):
file_fields.append(f_.name)
is_null = {'{}__isnull'.format(f_.name): True}
is_empty = {'{}__exact'.format(f_.name): ''}
filters &= Q(**is_null) | Q(**is_empty)
# only retrieve the models which have non-empty,
# non-null file fields
if file_fields:
files = model.objects.exclude(filters).values_list(
*file_fields,
flat=True
).distinct()
db_files.update(files)
# Get all files from the MEDIA_ROOT, recursively
media_root = getattr(settings, 'MEDIA_ROOT', None)
if media_root is not None:
for relative_root, dirs, files in os.walk(media_root):
for file_ in files:
# Compute the relative file path to the media directory,
# so it can be compared to the values from the db
relative_file = os.path.join(
os.path.relpath(relative_root, media_root), file_
)
physical_files.add(relative_file)
# Compute the difference and delete those files
deletables = physical_files - db_files
if deletables:
for file_ in deletables:
os.remove(os.path.join(media_root, file_))
# Bottom-up - delete all empty folders
for relative_root, dirs, files in os.walk(
media_root, topdown=False):
for dir_ in dirs:
if not os.listdir(os.path.join(relative_root, dir_)):
os.rmdir(os.path.join(relative_root, dir_))