Is there a way to cancel a deletion of record using django pre_delete signal?
example:
def on_delete(sender,**kwargs):
if not <some condition>:
#cancel the deletion
# else continue with the deletion
pre_delete.connect(on_delete,sender=MyModel)
and another question is there a way to say to a model "that before changing a file delete first the original file" because right now this is what I do(see code below) and I'm not sure if this is the best way to do it.
def on_save(sender,**kwargs):
obj = kwargs['instance']
try:
id = obj.pk
# find the file
original_file = sender.objects.get(pk=id)
# delete the original file before uploading a new file
original_file.file.delete()
except ....
pre_save.connect(on_save,sender=ModelWithFileUpload)
(in django 1.2 they automatically delete the file on change or on delete but in django 1.3 they removed this feature)
Thanks in advance
I would try a little hack workaround:
def on_delete(sender,**kwargs):
if not <some condition>:
raise Exception('Do not delete')#cancel the deletion
# else continue with the deletion
pre_delete.connect(on_delete,sender=MyModel)
and the view
def on_save(sender,**kwargs):
obj = kwargs['instance']
try:
id = obj.pk
# find the file
original_file = sender.objects.get(pk=id)
# delete the original file before uploading a new file
except ... :
# oder exceptions
try:
original_file.file.delete()
except:
pass #not deleted
pre_save.connect(on_save,sender=ModelWithFileUpload)
Raising exception in signal should brake delete() method execution while returning exception to the place it was invoked. You could create your own Exception subclass to except only certain type of exception (you almost never should use except with no args).
I know my answer comes a bit late, but the second part of this question is exactly what I needed a few days ago.
So, first things first:
Is there a way to cancel a deletion of record using django pre_delete signal?
Not really, except for the one proposed by thedk. And honestly, there shouldn't be any. Why? Because pre_delete is meant for an action that is suppose to happen before deleting an object. If you prevent the deletion, it is no longer pre_delete (notice the vicious circle?)
is there a way to say to a model that before changing a file delete first the original file?
Yes, there is, and you got it pretty much right. I created a more generic code, which will work for any model which has File objects associated (see below). However, you should forehand read why this behaviour was removed in Django 1.3 and see if it affects your logic in any way. It is mainly related to how you handle rollbacks and multiple references to the same file from different models.
def delete_files_from_instance(instance, field_names):
for field_name in field_names:
field_value = getattr(instance, field_name, None)
if field_value:
if isinstance(field_value, File):
try:
os.remove(field_value.path)
except OSError:
pass
@receiver(pre_delete)
def on_delete(sender, instance, **kwargs):
# When an object is deleted, all associated files are also removed
delete_files_from_instance(instance, sender._meta.get_all_field_names())
@receiver(pre_save)
def on_update(sender, instance, **kwargs):
# When an object is updated, if any media files are replaced, the old ones should be deleted.
from_fixture = 'raw' in kwargs and kwargs['raw'] # this prevents errors when loading files from fixtures
is_valid_app = sender._meta.app_label in VALID_APPS # Define what apps are targeted by your code
if is_valid_app and not from_fixture:
try:
old_instance = sender.objects.filter(pk=instance.id).first()
if old_instance and old_instance is not None:
delete_files_from_instance(old_instance, sender._meta.get_all_field_names())
except LookupError:
pass
Please bear in mind that this assumes that your delete/update action will be successful. In case it fails, you have permanently lost a file.
A better approach would be to handle file deletion in the post_save/post_delete signals, or to create a cron job which periodically cleans up all files which are no longer referenced from the database.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With