diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py
index 676e3f5af..b29465071 100644
--- a/netbox/netbox/views/generic/bulk_views.py
+++ b/netbox/netbox/views/generic/bulk_views.py
@@ -7,7 +7,7 @@ from django.contrib.contenttypes.fields import GenericRel
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist, ValidationError
from django.db import transaction, IntegrityError
-from django.db.models import ManyToManyField, ProtectedError
+from django.db.models import ManyToManyField, ProtectedError, RestrictedError
from django.db.models.fields.reverse_related import ManyToManyRel
from django.forms import HiddenInput, ModelMultipleChoiceField, MultipleHiddenInput
from django.http import HttpResponse
@@ -804,8 +804,8 @@ class BulkDeleteView(GetReturnURLMixin, BaseMultiObjectView):
obj.snapshot()
obj.delete()
- except ProtectedError as e:
- logger.info("Caught ProtectedError while attempting to delete objects")
+ except (ProtectedError, RestrictedError) as e:
+ logger.info(f"Caught {type(e)} while attempting to delete objects")
handle_protectederror(queryset, request, e)
return redirect(self.get_return_url(request))
diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py
index 99d8ff540..7c737aaf0 100644
--- a/netbox/netbox/views/generic/object_views.py
+++ b/netbox/netbox/views/generic/object_views.py
@@ -3,7 +3,7 @@ from copy import deepcopy
from django.contrib import messages
from django.db import transaction
-from django.db.models import ProtectedError
+from django.db.models import ProtectedError, RestrictedError
from django.shortcuts import redirect, render
from django.urls import reverse
from django.utils.html import escape
@@ -374,8 +374,8 @@ class ObjectDeleteView(GetReturnURLMixin, BaseObjectView):
try:
obj.delete()
- except ProtectedError as e:
- logger.info("Caught ProtectedError while attempting to delete object")
+ except (ProtectedError, RestrictedError) as e:
+ logger.info(f"Caught {type(e)} while attempting to delete objects")
handle_protectederror([obj], request, e)
return redirect(obj.get_absolute_url())
diff --git a/netbox/utilities/error_handlers.py b/netbox/utilities/error_handlers.py
index 1d3bdbafd..be579f4eb 100644
--- a/netbox/utilities/error_handlers.py
+++ b/netbox/utilities/error_handlers.py
@@ -1,16 +1,28 @@
from django.contrib import messages
+from django.db.models import ProtectedError, RestrictedError
from django.utils.html import escape
from django.utils.safestring import mark_safe
+from django.utils.translation import gettext_lazy as _
def handle_protectederror(obj_list, request, e):
"""
- Generate a user-friendly error message in response to a ProtectedError exception.
+ Generate a user-friendly error message in response to a ProtectedError or RestrictedError exception.
"""
- protected_objects = list(e.protected_objects)
- protected_count = len(protected_objects) if len(protected_objects) <= 50 else 'More than 50'
- err_message = f"Unable to delete {', '.join(str(obj) for obj in obj_list)}. " \
- f"{protected_count} dependent objects were found: "
+ if type(e) is ProtectedError:
+ protected_objects = list(e.protected_objects)
+ elif type(e) is RestrictedError:
+ protected_objects = list(e.restricted_objects)
+ else:
+ raise e
+
+ # Formulate the error message
+ err_message = _(
+ "Unable to delete {objects}. {count} dependent objects were found: ".format(
+ objects=', '.join(str(obj) for obj in obj_list),
+ count=len(protected_objects) if len(protected_objects) <= 50 else _('More than 50')
+ )
+ )
# Append dependent objects to error message
dependent_objects = []