私の答えが少し遅れていることは承知していますが、この質問の 2 番目の部分は、数日前にまさに私が必要としていたものです。
だから、まず最初に:
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 ジョブを作成することです。