But in the database I see the instance is never deleted. The file is correctly removed. If I comment out the kwargs["instance"].file.delete() line, the instance is correctly removed. Why?
my workaround:
def file_cleanup(sender, **kwargs):
path = Path(kwargs["instance"].file.path)
if path.is_file():
path.unlink()
I am trying to understand why the first “solution” did not work.
What happens if you change this from a post_delete sitgnal to a pre_delete signal?
From the docs for the post_delete signal:
instance
The actual instance being deleted.
Note that the object will no longer be in the database, so be very careful what you do with this instance.
(Note: I have no actual idea what is happening here, or what the effects may be of what you’re doing - I learned a long time ago to avoid signals except in those cases where they’re actually needed, and so I’ve never encountered anything like this.)
The delete method (you are calling) on FieldFile accepts a save parameter which defaults to True. So, in your example, the model object is saved after deleting file, thus recreating the object in database. If you pass save=False parameter when calling delete, the model object should not be recreated in database.
Side note: when checking for existence of file, using
you suppose the storage backend is a FileSystemStorage.
If you want your code to be agnostic from backend storage (for example if you change it to S3Storage from django-storages) and still want to have your cleaning function working for such backend, you could use following test :
if kwargs["instance"].file.storage.exists(kwargs["instance"].file.name):
def file_cleanup(sender, **kwargs):
from .models import NewLayoutResource
if isinstance(sender, NewLayoutResource):
if kwargs["instance"].file.storage.exists(kwargs["instance"].file.name):
kwargs["instance"].file.delete(save = False)
I thought using signals was smart, but after reading @KenWhitesell 's post I decided to go for:
def clean(self):
clean_data = super().clean()
if (self.has_changed() and "file" in self.initial.keys()) or clean_data["DELETE"] == True:
self.instance.delete_file()
return clean_data
which also works just fine and avoids all signals I would probably need some time to wrap my head around pre/post_delete signals