はじめに
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)