【问题标题】:how to cancel a delete in django signal如何取消 django 信号中的删除
【发布时间】:2011-04-11 16:27:54
【问题描述】:

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

提前致谢

【问题讨论】:

    标签: django django-signals


    【解决方案1】:

    我会尝试一些 hack 解决方法:

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

    【讨论】:

    • 我不会这样做。是的,它适用于自定义视图,但如果/当该异常被引发且未处理时,试图通过管理员删除对象的人将收到一个令人讨厌的错误。
    • 我不认为这在所有情况下都是一个 hack。当尝试删除只能在错误情况下发生时,抛出异常似乎是正确的反应。
    【解决方案2】:

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

    所以,第一件事:

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

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

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

    是的,有,而且你说得很对。我创建了一个更通用的代码,它适用于任何关联了 File 对象的模型(见下文)。但是,您应该提前read why 这种行为已在 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 作业来自数据库。

    【讨论】:

    • 如果在创建记录之前需要与第三方服务联系以获得唯一 ID,那么 pre_savepre_delete 似乎是完美的选择。你会推荐哪里?
    • 既然你说的是“创造之前”,那么pre_save应该是正确的地方。
    • 那么我建议从模型中覆盖save() 函数。任何异常都可以在那里处理。
    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-27
      • 1970-01-01
      • 1970-01-01
      • 2014-04-15
      • 2015-02-18
      • 2021-01-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多