Is there a way to cancel a deletion of record using django pre_delete signal?
是否有办法使用django pre_delete信号取消记录的删除?
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)
(在django 1.2中,它们在更改或删除时自动删除文件,但在django 1.3中,它们删除了这个特性)
Thanks in advance
谢谢提前
3 个解决方案
#1
1
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).
在signal中引发异常时,应该在将异常返回到被调用的位置时暂停delete()方法执行。您可以创建您自己的异常子类,只使用特定类型的异常(几乎不应该使用,除非没有args)。
#2
1
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?
是否有办法使用django pre_delete信号取消记录的删除?
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?)
不是真的,除了thedk提出的那个。老实说,不应该有。为什么?因为pre_delete是用于在删除对象之前执行的操作。如果您阻止了删除,它就不再是pre_delete(注意到恶性循环了吗?)
-
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.
是的,有,你说得很对。我创建了一个更通用的代码,它将适用于任何具有相关文件对象的模型(参见下面)。但是,您应该预先阅读Django 1.3中为何删除了这种行为,并查看它是否以任何方式影响您的逻辑。它主要与如何处理来自不同模型的相同文件的回滚和多个引用有关。
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.
更好的方法是处理post_save/post_delete信号中的文件删除,或者创建一个cron作业,定期清理数据库中不再引用的所有文件。
#3
0
This is not be possible when using the built-in Django signals. The "send()" and "send_robust()" methods on signals return a list of 2-tuples -- (receiver, response). So, if you have the proper code to handle the responses from each receiver, it is possible that you could prevent some action based on the return value of one signal handler.
当使用内置的Django信号时,这是不可能的。信号上的“send()”和“send_健壮()”方法返回一个2元组列表——(接收方、响应)。因此,如果您有合适的代码来处理来自每个接收者的响应,那么您可能可以防止基于一个信号处理程序的返回值的某些操作。
The contrib.comments app does this, allowing any receiver which returns False to "cancel" a signal action. See lines 111-120:
这款应用程序做到了这一点,允许任何返回False的接收器“取消”一个信号动作。见第120 - 111行:
However, the core Django code which issues the pre_delete, pre_save, etc signals, does not have any of this special handling. All those signals do is notify the receivers that something has happened.
但是,发出pre_delete、pre_save等信号的核心Django代码没有任何这种特殊处理。所有这些信号所做的就是通知接收方发生了什么事情。
#1
1
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).
在signal中引发异常时,应该在将异常返回到被调用的位置时暂停delete()方法执行。您可以创建您自己的异常子类,只使用特定类型的异常(几乎不应该使用,除非没有args)。
#2
1
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?
是否有办法使用django pre_delete信号取消记录的删除?
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?)
不是真的,除了thedk提出的那个。老实说,不应该有。为什么?因为pre_delete是用于在删除对象之前执行的操作。如果您阻止了删除,它就不再是pre_delete(注意到恶性循环了吗?)
-
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.
是的,有,你说得很对。我创建了一个更通用的代码,它将适用于任何具有相关文件对象的模型(参见下面)。但是,您应该预先阅读Django 1.3中为何删除了这种行为,并查看它是否以任何方式影响您的逻辑。它主要与如何处理来自不同模型的相同文件的回滚和多个引用有关。
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.
更好的方法是处理post_save/post_delete信号中的文件删除,或者创建一个cron作业,定期清理数据库中不再引用的所有文件。
#3
0
This is not be possible when using the built-in Django signals. The "send()" and "send_robust()" methods on signals return a list of 2-tuples -- (receiver, response). So, if you have the proper code to handle the responses from each receiver, it is possible that you could prevent some action based on the return value of one signal handler.
当使用内置的Django信号时,这是不可能的。信号上的“send()”和“send_健壮()”方法返回一个2元组列表——(接收方、响应)。因此,如果您有合适的代码来处理来自每个接收者的响应,那么您可能可以防止基于一个信号处理程序的返回值的某些操作。
The contrib.comments app does this, allowing any receiver which returns False to "cancel" a signal action. See lines 111-120:
这款应用程序做到了这一点,允许任何返回False的接收器“取消”一个信号动作。见第120 - 111行:
However, the core Django code which issues the pre_delete, pre_save, etc signals, does not have any of this special handling. All those signals do is notify the receivers that something has happened.
但是,发出pre_delete、pre_save等信号的核心Django代码没有任何这种特殊处理。所有这些信号所做的就是通知接收方发生了什么事情。