Commit 1581befa authored by chrisspen's avatar chrisspen
Browse files

Added management command to handle bulk file deletions.

parent 487c1697
...@@ -47,6 +47,13 @@ system, run: ...@@ -47,6 +47,13 @@ system, run:
$ python manage.py database_files_dump $ python manage.py database_files_dump
Note, that when a field referencing a file is cleared, the corresponding file
in the database and on the file system will not be automatically deleted.
To delete all files in the database and file system not referenced by any model
fields, run:
$ python manage.py database_files_cleanup
Test suite Test suite
---------- ----------
......
import os
from django.conf import settings
from django.core.files.storage import default_storage
from django.core.management.base import BaseCommand, CommandError
from django.db.models import FileField, ImageField, get_models
from database_files.models import File
class Command(BaseCommand):
args = ''
help = 'Deletes all files in the database that are not referenced by ' + \
'any model fields.'
def handle(self, *args, **options):
tmp_debug = settings.DEBUG
settings.DEBUG = False
names = set()
try:
for model in get_models():
for field in model._meta.fields:
if not isinstance(field, (FileField, ImageField)):
continue
# Ignore records with null or empty string values.
q = {'%s__isnull'%field.name:False}
xq = {field.name:''}
for row in model.objects.filter(**q).exclude(**xq):
file = getattr(row, field.name)
if file is None:
continue
if not file.name:
continue
names.add(file.name)
# Find all database files with names not in our list.
orphan_files = File.objects.exclude(name__in=names)
for f in orphan_files:
print 'Deleting %s...' % (f.name,)
default_storage.delete(f.name)
finally:
settings.DEBUG = tmp_debug
...@@ -6,6 +6,7 @@ from django.core.management.base import BaseCommand, CommandError ...@@ -6,6 +6,7 @@ from django.core.management.base import BaseCommand, CommandError
from django.db.models import FileField, ImageField from django.db.models import FileField, ImageField
from database_files.models import File from database_files.models import File
from database_files.utils import write_file
from optparse import make_option from optparse import make_option
...@@ -29,14 +30,10 @@ class Command(BaseCommand): ...@@ -29,14 +30,10 @@ class Command(BaseCommand):
for file in q: for file in q:
i += 1 i += 1
print '%i of %i' % (i, total) print '%i of %i' % (i, total)
fqfn = os.path.join(settings.MEDIA_ROOT, file.name) write_file(
fqfn = os.path.normpath(fqfn) file.name,
if os.path.isfile(fqfn) and not options['overwrite']: file.content.read(),
continue options['overwrite'])
dirs,fn = os.path.split(fqfn)
if not os.path.isdir(dirs):
os.makedirs(dirs)
open(fqfn, 'wb').write(file.content)
finally: finally:
settings.DEBUG = tmp_debug settings.DEBUG = tmp_debug
\ No newline at end of file
...@@ -5,8 +5,6 @@ from django.core.files.storage import default_storage ...@@ -5,8 +5,6 @@ from django.core.files.storage import default_storage
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from django.db.models import FileField, ImageField, get_models from django.db.models import FileField, ImageField, get_models
from optparse import make_option
class Command(BaseCommand): class Command(BaseCommand):
args = '' args = ''
help = 'Loads all files on the filesystem referenced by FileFields ' + \ help = 'Loads all files on the filesystem referenced by FileFields ' + \
......
...@@ -7,6 +7,7 @@ from django.core.files.storage import FileSystemStorage ...@@ -7,6 +7,7 @@ from django.core.files.storage import FileSystemStorage
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from database_files import models from database_files import models
from database_files.utils import write_file
class DatabaseStorage(FileSystemStorage): class DatabaseStorage(FileSystemStorage):
...@@ -57,11 +58,16 @@ class DatabaseStorage(FileSystemStorage): ...@@ -57,11 +58,16 @@ class DatabaseStorage(FileSystemStorage):
size = content.size size = content.size
except AttributeError: except AttributeError:
size = os.path.getsize(full_path) size = os.path.getsize(full_path)
content = content.read()
f = models.File.objects.create( f = models.File.objects.create(
content=content.read(), content=content,
size=size, size=size,
name=name, name=name,
) )
# Automatically write the change to the local file system.
if getattr(settings, 'DATABASE_FILES_FS_AUTO_WRITE', True):
write_file(name, content, overwrite=True)
#TODO:add callback to handle custom save behavior?
return self._generate_name(name, f.pk) return self._generate_name(name, f.pk)
def exists(self, name): def exists(self, name):
...@@ -77,7 +83,6 @@ class DatabaseStorage(FileSystemStorage): ...@@ -77,7 +83,6 @@ class DatabaseStorage(FileSystemStorage):
""" """
Deletes the file with filename `name` from the database and filesystem. Deletes the file with filename `name` from the database and filesystem.
""" """
full_path = self.path(name)
try: try:
models.File.objects.get_from_name(name).delete() models.File.objects.get_from_name(name).delete()
except models.File.DoesNotExist: except models.File.DoesNotExist:
......
import os
from django.conf import settings
def write_file(name, content, overwrite=False):
"""
Writes the given content to the relative filename under the MEDIA_ROOT.
"""
fqfn = os.path.join(settings.MEDIA_ROOT, name)
fqfn = os.path.normpath(fqfn)
if os.path.isfile(fqfn) and not overwrite:
return
dirs,fn = os.path.split(fqfn)
if not os.path.isdir(dirs):
os.makedirs(dirs)
open(fqfn, 'wb').write(content)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment