2011-04-11 20 views
11

有沒有辦法使用django pre_delete信號取消記錄刪除?如何取消django信號中的刪除

例如:

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的文件,他們取消了這一功能)

在此先感謝

回答

0

這是不可能的在Django信號。信號上的「send()」和「send_robust()」方法返回2元組列表(接收器,響應)。因此,如果您有正確的代碼來處理來自每個接收器的響應,則可能會根據一個信號處理器的返回值來阻止某些操作。

contrib.comments應用程序執行此操作,允許任何接收器返回False以「取消」信號操作。見lines 111-120

但是,發出pre_delete,pre_save等信號的core Django code沒有任何特殊的處理。所有這些信號都會通知接收方發生了某些事情。

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) 

提高異常信號應該剎車刪除()方法執行,同時返回例外,它被調用的地方。你可以創建你自己的Exception子類,除了只有某種類型的異常(你幾乎從不應該使用,除非沒有參數)。

+0

我不會這樣做。是的,它適用於自定義視圖,但有人試圖通過管理員刪除一個對象將得到一個令人討厭的錯誤,如果/當該異常引發和處理。 – shadfc 2011-04-16 22:06:23

+0

你是對的。正如我所說,這是醜陋的黑客。 – thedk 2011-04-18 12:17:38

+0

我不認爲這在任何情況下都是黑客行爲。當嘗試刪除只能發生在錯誤情況下時,拋出異常似乎是正確的反應。 – Teekin 2017-04-30 19:12:23

1

我知道我的答案有點晚了,但這個問題的第二部分是正好是我幾天前需要的。

所以,首先第一件事情:

  1. 有沒有辦法使用Django pre_delete信號取消刪除記錄?

不是真的,除非是由thedk提出的一個。說實話,應該沒有。爲什麼?因爲pre_delete適用於在刪除對象之前會發生的操作。如果你阻止刪除,它不再是pre_delete(注意惡性循環?)

  • 是有辦法要說到一個模型,改變文件之前首先刪除原始文件?

  • 是的,有,你得到它非常正確的。我創建了一個更通用的代碼,它可以用於任何有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作業。

    +0

    那麼在創建記錄之前需要與第三方服務通話的情況下,爲了獲得唯一的ID,那麼'pre_save'或'pre_delete'似乎是一個完美的地方。你會推薦什麼? – surfer190 2017-05-31 13:10:18

    +0

    既然你說過「創造之前」,那麼'pre_save'應該是正確的地方。 – AdelaN 2017-05-31 13:18:08

    +0

    是的,但如果服務失敗,我不希望模型被保存... – surfer190 2017-05-31 13:25:46