110
You can receive the pre_delete
or post_delete
signal (see @toto_ticoโs comment below) and call the delete() method on the FileField object, thus (in models.py):
class MyModel(models.Model):
file = models.FileField()
...
# Receive the pre_delete signal and delete the file associated with the model instance.
from django.db.models.signals import pre_delete
from django.dispatch.dispatcher import receiver
@receiver(pre_delete, sender=MyModel)
def mymodel_delete(sender, instance, **kwargs):
# Pass false so FileField doesn't save the model.
instance.file.delete(False)
58
Try django-cleanup
pip install django-cleanup
settings.py
INSTALLED_APPS = (
...
'django_cleanup.apps.CleanupConfig',
)
- [Django]-How to get the domain name of my site within a Django template?
- [Django]-How to get primary keys of objects created using django bulk_create
- [Django]-Foreign key from one app into another in Django
35
Django 1.5 solution: I use post_delete for various reasons that are internal to my app.
from django.db.models.signals import post_delete
from django.dispatch import receiver
@receiver(post_delete, sender=Photo)
def photo_post_delete_handler(sender, **kwargs):
photo = kwargs['instance']
storage, path = photo.original_image.storage, photo.original_image.path
storage.delete(path)
I stuck this at the bottom of the models.py file.
the original_image
field is the ImageField
in my Photo
model.
- [Django]-Python NameError: name 'include' is not defined
- [Django]-How to check if ManyToMany field is not empty?
- [Django]-Django โ makemigrations โ No changes detected
18
This code runs well on Django 1.4 also with the Admin panel.
class ImageModel(models.Model):
image = ImageField(...)
def delete(self, *args, **kwargs):
# You have to prepare what you need before delete the model
storage, path = self.image.storage, self.image.path
# Delete the model before the file
super(ImageModel, self).delete(*args, **kwargs)
# Delete the file after the model
storage.delete(path)
Itโs important to get the storage and the path before delete the model or the latter will persist void also if deleted.
- [Django]-How to run celery as a daemon in production?
- [Django]-Django โ how to visualize signals and save overrides?
- [Django]-DatabaseError: current transaction is aborted, commands ignored until end of transaction block?
17
You need to remove the actual file on both delete
and update
.
from django.db import models
class MyImageModel(models.Model):
image = models.ImageField(upload_to='images')
def remove_on_image_update(self):
try:
# is the object in the database yet?
obj = MyImageModel.objects.get(id=self.id)
except MyImageModel.DoesNotExist:
# object is not in db, nothing to worry about
return
# is the save due to an update of the actual image file?
if obj.image and self.image and obj.image != self.image:
# delete the old image file from the storage in favor of the new file
obj.image.delete()
def delete(self, *args, **kwargs):
# object is being removed from db, remove the file from storage first
self.image.delete()
return super(MyImageModel, self).delete(*args, **kwargs)
def save(self, *args, **kwargs):
# object is possibly being updated, if so, clean up.
self.remove_on_image_update()
return super(MyImageModel, self).save(*args, **kwargs)
- [Django]-Combining Django F, Value and a dict to annotate a queryset
- [Django]-How to add custom search box in Django-admin?
- [Django]-Token Authentication for RESTful API: should the token be periodically changed?
6
You may consider using a pre_delete or post_delete signal:
https://docs.djangoproject.com/en/dev/topics/signals/
Of course, the same reasons that FileField automatic deletion was removed also apply here. If you delete a file that is referenced somewhere else you will have problems.
In my case this seemed appropriate because I had a dedicated File model to manage all of my files.
Note: For some reason post_delete doesnโt seem to work right. The file got deleted, but the database record stayed, which is completely the opposite of what I would expect, even under error conditions. pre_delete works fine though.
- [Django]-How can I handle Exceptions raised by dango-social-auth?
- [Django]-How do I remove Label text in Django generated form?
- [Django]-How to concatenate strings in django templates?
3
Maybe itโs a little late. But the easiest way for me is to use a post_save signal. Just to remember that signals are excecuted even during a QuerySet delete process, but the [model].delete() method is not excecuted during the QuerySet delete process, so itโs not the best option to override it.
core/models.py:
from django.db import models
from django.db.models.signals import post_delete
from core.signals import delete_image_slide
SLIDE1_IMGS = 'slide1_imgs/'
class Slide1(models.Model):
title = models.CharField(max_length = 200)
description = models.CharField(max_length = 200)
image = models.ImageField(upload_to = SLIDE1_IMGS, null = True, blank = True)
video_embed = models.TextField(null = True, blank = True)
enabled = models.BooleanField(default = True)
"""---------------------------- SLIDE 1 -------------------------------------"""
post_delete.connect(delete_image_slide, Slide1)
"""--------------------------------------------------------------------------"""
core/signals.py
import os
def delete_image_slide(sender, **kwargs):
slide = kwargs.get('instance')
try:
os.remove(slide.image.path)
except:
pass
- [Django]-Speeding up Django Testing
- [Django]-Django Installed Apps Location
- [Django]-DateTimeField doesn't show in admin system
1
This functionality will be removed in Django 1.3 so I wouldnโt rely on it.
You could override the delete
method of the model in question to delete the file before removing the entry from the database completely.
Edit:
Here is a quick example.
class MyModel(models.Model):
self.somefile = models.FileField(...)
def delete(self, *args, **kwargs):
somefile.delete()
super(MyModel, self).delete(*args, **kwargs)
- [Django]-Django select only rows with duplicate field values
- [Django]-Naming convention for Django URL, templates, models and views
- [Django]-How to resolve "django.core.exceptions.ImproperlyConfigured: Application labels aren't unique, duplicates: foo" in Django 1.7?
1
Using the post_delete is for sure the right way to go. Sometimes though things can go wrong, and files donโt get deleted. There is of course the case that you have a bunch of old files that werenโt deleted before post_delete was used. I created a function that deletes files for objects based on if the file the object references does not exist then delete object, if the file does not have an object, then also delete, also it can delete based on an โactiveโ flag for an object.. Something I added to most of my models. You have to pass it the objects you want to check, the path to the objects files, the file field and a flag to delete inactive objects:
def cleanup_model_objects(m_objects, model_path, file_field='image', clear_inactive=False):
# PART 1 ------------------------- INVALID OBJECTS
#Creates photo_file list based on photo path, takes all files there
model_path_list = os.listdir(model_path)
#Gets photo image path for each photo object
model_files = list()
invalid_files = list()
valid_files = list()
for obj in m_objects:
exec("f = ntpath.basename(obj." + file_field + ".path)") # select the appropriate file/image field
model_files.append(f) # Checks for valid and invalid objects (using file path)
if f not in model_path_list:
invalid_files.append(f)
obj.delete()
else:
valid_files.append(f)
print "Total objects", len(model_files)
print "Valid objects:", len(valid_files)
print "Objects without file deleted:", len(invalid_files)
# PART 2 ------------------------- INVALID FILES
print "Files in model file path:", len(model_path_list)
#Checks for valid and invalid files
invalid_files = list()
valid_files = list()
for f in model_path_list:
if f not in model_files:
invalid_files.append(f)
else:
valid_files.append(f)
print "Valid files:", len(valid_files)
print "Files without model object to delete:", len(invalid_files)
for f in invalid_files:
os.unlink(os.path.join(model_path, f))
# PART 3 ------------------------- INACTIVE PHOTOS
if clear_inactive:
#inactive_photos = Photo.objects.filter(active=False)
inactive_objects = m_objects.filter(active=False)
print "Inactive Objects to Delete:", inactive_objects.count()
for obj in inactive_objects:
obj.delete()
print "Done cleaning model."
This is how you can use this:
photos = Photo.objects.all()
photos_path, tail = ntpath.split(photos[0].image.path) # Gets dir of photos path, this may be different for you
print "Photos -------------->"
cleanup_model_objects(photos, photos_path, file_field='image', clear_inactive=False) # image file is default
- [Django]-Django admin: how to sort by one of the custom list_display fields that has no database field
- [Django]-Override default queryset in Django admin
- [Django]-Homepage login form Django
0
make sure you write โselfโ before the file. so example above should be
def delete(self, *args, **kwargs):
self.somefile.delete()
super(MyModel, self).delete(*args, **kwargs)
Iโve forgotten the โselfโ before my file and that didnโt work as it was looking in the global namespace.
- [Django]-Check permission inside a template in Django
- [Django]-TypeError: data.forEach is not a function
- [Django]-Django error: got multiple values for keyword argument
0
If you already have number of unused files in your project and want to delete them, you can use django utility django-unused-media
- [Django]-STATIC_ROOT vs STATIC_URL in Django
- [Django]-What's the point of Django's collectstatic?
- [Django]-ImproperlyConfigured: You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings
0
Django 2.x Solution:
Thereโs no need to install any packages! Itโs very easy to handle in Django 2. Iโve tried following solution using Django 2 and SFTP Storage (however I think it would work with any storages)
First write a Custom Manager. So if you want to be able to delete files of a model by using objects
methods, you must write and use a [Custom Manager][3] (for overriding delete()
method of objects
):
class CustomManager(models.Manager):
def delete(self):
for obj in self.get_queryset():
obj.delete()
Now you must delete image
before deleting deleting the model itself and for assigning the CustomManager
to the model, you must initial objects
inside your model:
class MyModel(models.Model):
image = models.ImageField(upload_to='/pictures/', blank=True)
objects = CustomManager() # add CustomManager to model
def delete(self, using=None, keep_parents=False):
objects = CustomManager() # just add this line of code inside of your model
def delete(self, using=None, keep_parents=False):
self.image.storage.delete(self.song.name)
super().delete()
- [Django]-Django Model MultipleChoice
- [Django]-Django โ Clean permission table
- [Django]-Django reverse lookup of foreign keys
-1
I may have a special case since I am using the upload_to option on my file field with dynamic directory names but the solution I found was to use os.rmdir.
In models:
import os
โฆ
class Some_Model(models.Model):
save_path = models.CharField(max_length=50)
...
def delete(self, *args,**kwargs):
os.rmdir(os.path.join(settings.MEDIA_ROOT, self.save_path)
super(Some_Model,self).delete(*args, **kwargs)
- [Django]-How to format time in django-rest-framework's serializer?
- [Django]-ValueError: The field admin.LogEntry.user was declared with a lazy reference
- [Django]-What is pip install -q -e . for in this Travis-CI build tutorial?