如何取消 django 信号中的删除

发布于 2024-10-31 08:43:45 字数 755 浏览 1 评论 0原文

有没有办法使用 django pre_delete 信号取消记录删除?

示例:

def on_delete(sender,**kwargs):
  if not <some condition>:
    #cancel the deletion
 # else continue with the deletion
pre_delete.connect(on_delete,sender=MyModel)

另一个问题是有没有办法对模型说“在更改文件之前先删除原始文件”,因为现在这就是我所做的(参见下面的代码)并且我不确定这是否是最好的方法。

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)

(在 django 1.2 中,他们在更改或删除时自动删除文件,但在 django 1.3 中,他们删除了此功能)

提前致谢

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

眼角的笑意。 2024-11-07 08:43:46

使用内置 Django 信号时这是不可能的。信号上的“send()”和“send_robust()”方法返回一个二元组列表——(接收者,响应)。因此,如果您有正确的代码来处理来自每个接收器的响应,则您可能可以根据一个信号处理程序的返回值来阻止某些操作。

contrib.comments 应用程序执行此操作,允许任何返回 False 的接收器“取消”信号操作。请参阅 第 111-120 行

但是, 核心 Django 代码 没有任何这种特殊处理。所有这些信号的作用就是通知接收者发生了一些事情。

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.

The contrib.comments app does this, allowing any receiver which returns False to "cancel" a signal action. See lines 111-120:

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.

尘世孤行 2024-11-07 08:43:46

我会尝试一些黑客解决方法:

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)

并且视图

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)

在信号中引发异常应该制动delete()方法执行,同时将异常返回到调用它的地方。您可以创建自己的 Exception 子类来仅排除某些类型的异常(您几乎永远不应该使用不带参数的 except)。

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).

魄砕の薆 2024-11-07 08:43:46

我知道我的回答有点晚了,但这个问题的第二部分正是我几天前所需要的。

所以,首先要做的事情是:

  1. 有没有办法使用 django pre_delete 信号取消记录删除?

不是真的,除了 dk 提出的那个。老实说,不应该有任何。为什么?因为 pre_delete 意味着在删除对象之前发生的操作。如果你阻止删除,它就不再是pre_delete(注意到恶性循环了吗?)

  1. 有没有办法告诉模型在更改文件之前先删除原始文件?

是的,有,而且你说得非常正确。我创建了一个更通用的代码,它将适用于任何具有关联的 File 对象的模型(见下文)。但是,您应该正手 阅读为什么此行为在 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

请记住,这假设您的删除/更新操作将会成功。如果失败,您将永久丢失文件。

更好的方法是在 post_save/post_delete 信号中处理文件删除,或者创建一个 cron 作业来定期清理数据库中不再引用的所有文件。

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:

  1. 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?)

  1. 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文