2011-04-11 16 views
11

¿Hay alguna manera de cancelar una eliminación de registros utilizando la señal django pre_delete?cómo cancelar una eliminación en la señal de django

ejemplo:

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

y otra pregunta ¿existe una manera de decir a un modelo "que antes de cambiar de borrado de archivos en primer lugar el archivo original" porque en este momento esto es lo que hago (véase el código a continuación) y no estoy seguro de si esta es la mejor manera de hacerlo.

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) 

(en Django 1.2 que eliminar automáticamente el archivo en el cambio o el borrado pero en Django 1.3 sacaron esta característica)

Gracias de antemano

Respuesta

0

Esto no es posible cuando el uso de la incorporada -en señales de Django. Los métodos "send()" y "send_robust()" en las señales devuelven una lista de 2-tuplas - (receptor, respuesta). Por lo tanto, si tiene el código adecuado para manejar las respuestas de cada receptor, es posible que pueda evitar alguna acción basada en el valor de retorno de un manejador de señal.

La aplicación contrib.comments hace esto, permitiendo que cualquier receptor que devuelve False "cancele" una acción de señal. Ver lines 111-120:

Sin embargo, el core Django code que emite las señales pre_delete, pre_save, etc., no tiene ninguno de este manejo especial. Todas esas señales hacen es notificar a los receptores que algo ha sucedido.

1

me gustaría probar un poco de solución 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) 

y la vista

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) 

El aumento de excepción en la señal debe freno de borrar() la ejecución del método cuando regresaba excepción al lugar que fue invocada. Podrías crear tu propia subclase de Excepción para exceptuar solo cierto tipo de excepción (casi nunca deberías usarlo excepto sin argumentos).

+0

Yo no haría esto. Sí, funciona para la vista personalizada, pero alguien que intenta eliminar un objeto a través del administrador obtendrá un error desagradable si/cuando esa excepción se plantea y no se maneja. – shadfc

+0

Tienes toda la razón. Como dije, este es un hack feo. – thedk

+0

No creo que esto sea un hack en todas las circunstancias. Cuando el intento de eliminación solo puede ocurrir en circunstancias erróneas, lanzar una excepción parece ser la reacción correcta. – Teekin

1

Sé que mi respuesta llega un poco tarde, pero la segunda parte de esta pregunta es exactamente lo que necesitaba hace unos días.

Por lo tanto, lo primero es lo primero:

  1. ¿Hay una manera de cancelar una supresión de registro usando la señal pre_delete Django?

En realidad no, a excepción de la propuesta por thedk. Y, sinceramente, no debería haber ninguno. ¿Por qué? Porque pre_delete está destinado a una acción que se supone que debe ocurrir antes de eliminar un objeto. Si evita la eliminación, ya no es pre_delete (observe el círculo vicioso?)

  1. hay una manera de decir a un modelo que antes de cambiar un archivo de borrar el fichero original?

Sí, existe, y lo tienes más o menos bien. Creé un código más genérico, que funcionará para cualquier modelo que tenga objetos de archivo asociados (ver a continuación). Sin embargo, debe forehand read why este comportamiento fue eliminado en Django 1.3 y ver si afecta a su lógica de ninguna manera. Se relaciona principalmente con la forma en que manejas los retrocesos y las múltiples referencias al mismo archivo de diferentes modelos.

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 

Por favor, tenga en cuenta que se supone que su acción/actualización de eliminación tendrá éxito. En caso de que falle, ha perdido un archivo de forma permanente.

Un mejor enfoque sería para manejar la eliminación de archivos en el post_delete post_save/ señales, o para crear una tarea programada que limpia periódicamente de todos los archivos que ya no se hace referencia a partir de la base de datos.

+0

Bueno, en el caso de necesitar hablar con un servicio de terceros antes de crear un registro, digamos para obtener una ID única, entonces un 'pre_save' o' pre_delete' parece ser el lugar perfecto. ¿Dónde recomendarías? – surfer190

+0

Como dijiste "antes de la creación", entonces "pre_save" debería ser el lugar correcto. – AdelaN

+0

Sí, pero si el servicio falla, no me gustaría que se guarde el modelo ... – surfer190

Cuestiones relacionadas