Last active
November 24, 2017 18:09
-
-
Save barseghyanartur/f0af5ac2caac9462b7d2 to your computer and use it in GitHub Desktop.
Clean media by deleting those files which are no more referenced by any FileField (django command)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
import os | |
import sys | |
from six.moves import input | |
from django.conf import settings | |
from django.core.management.base import BaseCommand, CommandError | |
from django.apps import apps | |
from django.db import models | |
class Command(BaseCommand): | |
"""""" | |
help = 'Clean media by deleting those files which are no more ' \ | |
'referenced by any FileField' | |
def add_arguments(self, parser): | |
"""""" | |
parser.add_argument('--noinput', | |
action = 'store_true', | |
default = False, | |
help = 'Clean media by deleting those files which are no more ' | |
'referenced by any FileField without asking for confirmation' | |
) | |
def handle(self, *args, **options): | |
"""""" | |
#print(options) | |
# Exclude cache folder by default since generally it's used by | |
# third-party apps | |
exclude_paths = getattr(settings, 'CLEANUP_EXCLUDE_PATHS', ['cache']) | |
exclude_roots = [ | |
os.path.abspath(os.path.normpath(os.path.join(settings.MEDIA_ROOT, | |
path))) | |
for path in exclude_paths | |
] | |
# Build a list of all media files used by our model | |
models_files = set([]) | |
for model in apps.get_models(): | |
model_objects = list(model.objects.all()) | |
model_file_fields = [] | |
model_files = [] | |
for field_name in model._meta.get_all_field_names(): | |
field = model._meta.get_field(field_name) | |
if isinstance(field, models.FileField): | |
model_file_fields.append(str(field_name)) | |
if not len(model_file_fields): | |
continue | |
#print(u'') | |
#print(u'%s %s' % (model.__name__, model_file_fields,)) | |
for obj in model_objects: | |
for field in model_file_fields: | |
file = getattr(obj, field, '') | |
if file: | |
#print(file.path) | |
models_files.add(os.path.abspath(file.path)) | |
unref_files = set([]) | |
for root, dirs, files in os.walk(os.path.abspath(settings.MEDIA_ROOT)): | |
exclude_root = False | |
for excluded_root in exclude_roots: | |
if root.find(excluded_root) == 0: | |
exclude_root = True | |
break | |
if exclude_root: | |
continue | |
for file in files: | |
file_path = os.path.abspath(os.path.join(root, file)) | |
if not file_path in models_files: | |
unref_files.add(file_path) | |
for file in unref_files: | |
print('Unreferenced file found: %s' % (file)) | |
num_unref_files = len(unref_files) | |
print('%s unreferenced file%s found' | |
'' % (num_unref_files, 's' if num_unref_files else '',)) | |
if num_unref_files: | |
remove_unref_files = True \ | |
if options['noinput'] \ | |
else (input('Remove all unreferenced files? (y/N) ' | |
'').lower().find('y') == 0) | |
if remove_unref_files: | |
for file in unref_files: | |
try: | |
os.remove(file) | |
print('Removed unreferenced file: %s' % (file)) | |
except Exception as err: | |
print('Failed to remove unreferenced file: ' | |
'%s due to %s' % (file, str(err))) | |
continue | |
return |
Thanks for the fixed, by the way this script doesn't work anymore with django >= 1.10
:
AttributeError: 'Options' object has no attribute 'get_all_field_names'
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Summary of changes (from original):
list
withset
) for which thein
works much faster.CLEANUP_EXCLUDE_PATHS
of django settings. Fall back to ['cache'] by default.