'Django: ProtectedError exception handling is not working
I am trying to handle ProtectedError exception and try to post a custom error message in my template.
def delete(self, request, *args, **kwargs):
obj = self.get_object()
get_success_url = self.get_success_url()
try:
obj.delete()
messages.success(self.request, self.success_message % obj.__dict__)
except ProtectedError:
messages.success(self.request, "can't delete")
return super().delete(request, *args, **kwargs)
without ProtectedError it is sending me back to my list page with delete successfully message but for ProtectedError it is sending me to some generic error page with ProtectedError at /settings/currency/1/delete/ message.
Thanks.
Solution 1:[1]
As I see it, on both cases your return is the same:
return super().delete(request, *args, **kwargs)
Instead you on except, raise the error:
raise ProtectedError('Cannot remove meta user instances', None)
or something like:
try:
obj.delete()
return JsonResponse({})
except ProtectedError as e:
return self.status_msg(e[0], status=405)
Take a look at those examples
Solution 2:[2]
Another optional, you can also handle it by creating new decorator:
from functools import wraps
from django.utils.translation import gettext_lazy as _
from django.db.models.deletion import ProtectedError
from rest_framework.exceptions import PermissionDenied
def protected_error_as_api_error():
"""
Decorator to handle all `ProtectedError` error as API Error,
which mean, converting from error 500 to error 403.
"""
def decorator(func):
@wraps(func)
def wrapper(request, *args, **kwargs):
try:
return func(request, *args, **kwargs)
except ProtectedError as error:
raise PermissionDenied(_('Action Denied: The selected object is being used '
'by the system. Deletion not allowed.'))
return wrapper
return decorator
Usage example:
from django.utils.decorators import method_decorator
from yourapp.decorators.protected_error_as_api_error import protected_error_as_api_error
@method_decorator(protected_error_as_api_error())
def delete(self, request, *args, **kwargs):
....
# OR
@method_decorator(protected_error_as_api_error())
def destroy(self, request, *args, **kwargs):
....
# OR
@action(methods=['delete'], ...):
@method_decorator(protected_error_as_api_error())
def your_view_name(self, request, *args, **kwargs):
....
Solution 3:[3]
CBV DeleteView uses a form (http://ccbv.co.uk/projects/Django/4.0/django.views.generic.edit/DeleteView/) which is recommended way to run validation and handle errors.
def post(self, request, *args, **kwargs):
# Set self.object before the usual form processing flow.
# Inlined because having DeletionMixin as the first base, for
# get_success_url(), makes leveraging super() with ProcessFormView
# overly complex.
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
Just define a custom form class with custom clean method and only field with id to retrieve the object:
https://docs.djangoproject.com/en/4.0/ref/forms/api/#django.forms.Form.clean
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Nikita Kosych |
Solution 2 | binpy |
Solution 3 |