Исключение ProtectedError

Исключение ProtectedError относится к модулю django.db.models и возникает при попытке удаления объекта модели, если на него ссылаются другие объекты через внешний ключ с установленным параметром on_delete в значение PROTECT. Это исключение наследуется от django.db.IntegrityError. При возникновении исключения в атрибуте protected_objects содержится список объектов, которые препятствуют удалению.

Синтаксис

pythonfrom django.db.models import PROTECT
from django.db import models

class ParentModel(models.Model):
  name = models.CharField(max_length=100)

class ChildModel(models.Model):
  parent = models.ForeignKey(
    ParentModel,
    on_delete=PROTECT
  )
  title = models.CharField(max_length=100)

Пример . Возникновение исключения

Рассмотрим модели блога, где категория защищена от удаления, если на неё ссылаются статьи:

pythonfrom django.db import models
from django.db.models import PROTECT

class Category(models.Model):
  name = models.CharField(max_length=100)

class Article(models.Model):
  title = models.CharField(max_length=100)
  category = models.ForeignKey(
    Category,
    on_delete=PROTECT
  )
  content = models.TextField()

Попытка удалить категорию, к которой привязаны статьи, приведёт к возникновению исключения:

pythonfrom myapp.models import Category, Article

category = Category.objects.get(id=1)
articles = Article.objects.filter(category=category)

print(f"Articles in category: {articles.count()}")
category.delete()

Результат выполнения кода:

python"Articles in category: 3"
"django.db.models.deletion.ProtectedError: Cannot delete some instances of model 'Category' because they are referenced through protected foreign keys: 'Article.category'"

Пример . Обработка исключения

Исключение можно перехватить и обработать, чтобы корректно сообщить пользователю о невозможности удаления:

pythonfrom django.db.models.deletion import ProtectedError
from myapp.models import Category

try:
  category = Category.objects.get(name="Technology")
  category.delete()
  print("Category deleted successfully")

except ProtectedError as e:
  print("Cannot delete category with related objects:")
  for obj in e.protected_objects:
    print(f"- {obj} (ID: {obj.id})")

Результат выполнения кода:

python"Cannot delete category with related objects:"
"- Article: Django Basics (ID: 1)"
"- Article: Python Web (ID: 2)"
"- Article: Database Design (ID: 3)"

Пример . С несколькими защищёнными связями

Если модель имеет несколько защищённых внешних ключей, то при удалении возникнет исключение с указанием всех препятствующих объектов:

pythonfrom django.db import models
from django.db.models import PROTECT

class User(models.Model):
  username = models.CharField(max_length=100)

class Post(models.Model):
  title = models.CharField(max_length=100)
  author = models.ForeignKey(
    User,
    on_delete=PROTECT,
    related_name="posts"
  )

class Comment(models.Model):
  text = models.TextField()
  post = models.ForeignKey(
    Post,
    on_delete=PROTECT,
    related_name="comments"
  )
  user = models.ForeignKey(
    User,
    on_delete=PROTECT,
    related_name="comments"
  )

Попытка удалить пользователя, у которого есть посты или комментарии, вызовет исключение:

pythonfrom django.db.models.deletion import ProtectedError
from myapp.models import User

try:
  user = User.objects.get(username="john_doe")
  user.delete()

except ProtectedError as e:
  print(f"Protected objects: {e.protected_objects}")

Результат выполнения кода:

python"Protected objects: [<Post: Django Tutorial>, <Comment: Great post!>]"

Смотрите также

  • исключение IntegrityError,
    базовое исключение для всех ошибок целостности данных
  • исключение RestrictedError,
    возникающее при попытке удаления с ограничениями
  • исключение DoesNotExist,
    возникающее при отсутствии запрашиваемого объекта
  • исключение ObjectDoesNotExist,
    базовое исключение для отсутствующих объектов модели