1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Djangoで削除時に外部キー制約によって例外が発生する問題の対処法

Last updated at Posted at 2024-03-02

Djangoで汎用View django.views.generic.DeleteView を用いた削除時に外部キー制約によってProtectedErrorの例外が発生するのをキャッチする方法を紹介します。

※ これは2020/02/05に個人ブログで公開した記事を移植し、CC0-1.0で提供しています。情報は古い可能性があります。

PROTECT されている場合に削除しようとする場合の挙動

Djangoでは、モデルに外部キーを設定できます。モデルの第二引数では、参照先が削除等された場合の挙動が設定できます。

class Comment(models.Model):
 user = models.ForeignKey(User, models.PROTECT)

ここで、参照先を削除できなくする models.PROTECT という設定ができます。この設定をした場合、参照先は削除できなくなります。

そして、公式ドキュメントにはこのような記述があります。

Prevent deletion of the referenced object by raising ProtectedError, a subclass of django.db.IntegrityError.

削除を試みると、 ProtectedError例外が発生するという仕様です。例外ということは、誤って削除が試みられると500エラーとなってしまいます。

汎用 View でシンプルに実装したい

一方で、せっかくDjangoで実装するのですから、汎用Viewでリレーション確認などの記述無しでシンプルに削除したいですよね。そこで次のように構築したとします。

class UserDeleteView(DeleteView):
  model = User
  success_url = reverse_lazy('user-list')

ここで先程示したように他のモデルからPROTECTされていた場合、UserDeleteViewはProtectedError例外エラー(500エラー)を返してしまいます。

例外をキャッチする

Viewでは例外をキャッチするのみの実装とします。

ほとんど汎用View使う理由がないような感じはしますが、post関数を上書きします。

class UserDeleteView(DeleteView):
  model = User
  success_url = reverse_lazy('user-list')

  def post(self, request, *args, **kwargs):
    try:
      obj = self.get_object()
      obj.delete()
    except models.ProtectedError as e:
      messages.error(request, f'{obj}」は紐付けられているため削除できません。')
      return redirect('user-list')

これで、ProtectedErrorが発生したら削除されず、フラッシュメッセージが表示されます。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?