如何取消django信号中的删除

时间:2011-04-11 16:27:55

标签: django django-signals

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

提前致谢

3 个答案:

答案 0 :(得分: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子类,除了只有某种类型的异常(除了没有args之外你几乎不应该使用)。

答案 1 :(得分:1)

我知道我的答案有点迟了,但这个问题的第二部分是完全几天前我需要的。

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

  1.   

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

  2. 不是真的,除了thedk提出的那个。老实说,不应该是任何人。为什么?因为 pre_delete 适用于在删除对象之前发生的操作。如果你阻止删除,它不再是 pre_delete (注意恶性循环?)

    1.   

      有没有办法对模型说明在更改文件之前先删除原始文件?

    2. 是的,有,你得到的非常正确。我创建了一个更通用的代码,它适用于任何与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作业来自数据库。

答案 2 :(得分:0)

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

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

但是,发出pre_delete,pre_save等信号的core Django code没有任何特殊处理。所有这些信号都是通知接收者已发生的事情。