我知道我的回答来得有点晚,但是这个问题的第二部分正是我几天前所需要的。
所以,首先要做的事情是:
有没有办法使用 django pre_delete 信号取消删除记录?
不是真的,除了thedk提出的那个。老实说,不应该有。为什么?因为pre_delete用于在删除对象之前假设发生的操作。如果阻止删除,则不再是pre_delete(注意恶性循环?)
有没有办法对模型说在更改文件之前先删除原始文件?
是的,有,而且你说得很对。我创建了一个更通用的代码,它适用于任何关联了 File 对象的模型(见下文)。但是,您应该提前阅读为什么在 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 作业。