我知道我的回答来晚了,但这个问题的第二部分正是我几天前需要的。
所以,第一件事:
有没有办法使用 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 作业来自数据库。