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?

More than 5 years have passed since last update.

django-rest-framework 参照整合性制約をチェックしてからオブジェクトを destroy(削除) する

Posted at

はじめに

Django Modelには 外部キー列のon_deleteを指定してレコードが削除された時の動作を指定することができます。

django.db.models.ForeignKey.on_delete - https://docs.djangoproject.com

この内PROTECTを指定した場合についてのお話です。
PROTECTを指定した場合は、ProtectedError 例外が発生し参照されているオブジェクトの削除が出来なくなります。

参照整合性制約に任せておけば削除は防止されますが、DELETE文のコストが無駄になってしまうので、
ここでは、事前に参照されているテーブルをDELETE文を発行することなく事前にチェックしましょうってことで作成したMixinのサンプルです。

拡張 DestroyModelMixinの作成

既存のDestroyModelMixinを継承します。
削除検証する抽象メソッドvalidate_destroyを定義します。
destroyをオーバーライドし、validate_destroyを呼び出し、ProtectedErrorを捉えたらValidationErrorを発生させます。

from abc import abstractmethod
from django.db.models.deletion import ProtectedError
from rest_framework.mixins import DestroyModelMixin
from rest_framework.validators import ValidationError


class ExDestroyModelMixin(DestroyModelMixin):
    @abstractmethod
    def validate_destroy(self, request, *args, **kwargs):
        raise NotImplementedError()

    def destroy(self, request, *args, **kwargs):
        try:
            self.validate_destroy(request, *args, **kwargs)
        except ProtectedError:
            # 外部キー参照エラー
            raise ValidationError({
                'protectedError': [
                    '既に他のデータから参照されているデータが含まれているため、削除できません。'
                ]
            })

        return super().destroy(request, *args, **kwargs)

使い方

使いたいビューセットで作成したExDestroyModelMixinをmixinします。
ビューセット側では抽象メソッドであるvalidation_destroyを実装します。

ここでは、Customerの子テーブルであるCustomerOfficeを検索しデータが存在すればProtectedErrorを発生させています。

from django.db.models.deletion import ProtectedError
from rest_framework import viewsets
from xxxxx.models import CustomerOffice
from api.views.mixins import ExDestroyModelMixin


class CustomerViewSet(viewsets.ModelViewSet, ExDestroyModelMixin):
    def validate_destroy(self, request, *args, **kwargs):
        queryset = CustomerOffice.objects.filter(customer_id=kwargs['pk'])
        if queryset.exists():
            raise ProtectedError('営業所テーブルから参照されているため、削除することができません。', queryset)
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?