13

有没有办法使用 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 中,他们删除了此功能)

提前致谢

4

3 回答 3

1

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

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 没有参数)。

于 2011-04-11T23:17:12.693 回答
1

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

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

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

不是真的,除了thedk提出的那个。老实说,不应该有。为什么?因为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 作业。

于 2016-03-18T17:39:13.200 回答
0

这在使用内置 Django 信号时是不可能的。信号上的“send()”和“send_robust()”方法返回一个 2 元组列表——(接收者,响应)。因此,如果您有适当的代码来处理来自每个接收器的响应,那么您可能会阻止基于一个信号处理程序的返回值的某些操作。

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

但是,发出 pre_delete、pre_save 等信号的核心 Django 代码没有任何这种特殊处理。所有这些信号所做的就是通知接收器发生了什么事。

于 2011-04-11T18:18:23.997 回答