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:
-
¿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?)
-
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.
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
Tienes toda la razón. Como dije, este es un hack feo. – thedk
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