13

django pre_delete シグナルを使用してレコードの削除をキャンセルする方法はありますか?

例:

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

もう1つの質問は、「ファイルを変更する前に最初に元のファイルを削除する」というモデルに言う方法があります.やれ。

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)

(django 1.2では、変更または削除時にファイルを自動的に削除しますが、django 1.3ではこの機能を削除しました)

前もって感謝します

4

3 に答える 3

1

ちょっとしたハックの回避策を試してみます:

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)

とビュー

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)

シグナルで例外を発生させると、delete() メソッドの実行が中断され、呼び出された場所に例外が返されます。独自の Exception サブクラスを作成して、特定のタイプの例外のみを除外することができます (引数なしで except を使用することはほとんどありません)。

于 2011-04-11T23:17:12.693 に答える
1

私の答えが少し遅れていることは承知していますが、この質問の 2 番目の部分は、数日前にまさに私が必要としていたものです。

だから、まず最初に:

  1. django pre_delete シグナルを使用してレコードの削除をキャンセルする方法はありますか?

thedk によって提案されたものを除いて、そうではありません。そして正直なところ、何もないはずです。なんで?pre_deleteは、オブジェクトを削除する前に発生すると想定されるアクションを対象としているためです。削除を防止すると、もはやpre_deleteではありません(悪循環に気づきましたか?)

  1. ファイルを変更する前に、最初に元のファイルを削除することをモデルに伝える方法はありますか?

はい、あります。ほぼ正解です。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 ジョブを作成することです。

于 2016-03-18T17:39:13.200 に答える
0

組み込みの Django シグナルを使用している場合、これは不可能です。シグナルの「send()」および「send_robust()」メソッドは、2 つのタプル (受信者、応答) のリストを返します。したがって、各レシーバーからの応答を処理する適切なコードがあれば、1 つのシグナル ハンドラーの戻り値に基づいて何らかのアクションを防止できる可能性があります。

contrib.comments アプリはこれを行い、False を返す受信者がシグナル アクションを「キャンセル」できるようにします。111 ~ 120 行を参照してください。

ただし、pre_delete、pre_save などのシグナルを発行するコア Django コードには、この特別な処理はありません。これらの信号はすべて、何かが起こったことを受信者に通知するだけです。

于 2011-04-11T18:18:23.997 に答える